Home > clojure > On Clojure, Swig, and the UnsatisfiedLinkError

On Clojure, Swig, and the UnsatisfiedLinkError

September 16th, 2011 Leave a comment Go to comments

This week I did some Clojure work with a swigged-up C++ library that works great from Java. I was surprised when things didn’t go as smoothly in Clojure. In particular, loading native libraries in the REPL doesn’t seem to work. I’m writing this down in case someone else has the same problem in hopes that it’ll save them an hour or two of head-scratching and library path fiddling.

So, to set the stage, let’s make a tiny Swig library, compile it and try using it from the REPL. I’m using Leiningen just to save some time:

    $ lein new swiggy
    $ cd swiggy
    $ cat >swiggy.i <<EOF
    > %module Swiggy
    > %{
    >   int get_something() { return 42; }
    > %}
    > int get_something();
    > EOF
    $ swig -java -package swiggy -outdir src/swiggy swiggy.i
    $ gcc -I$JAVA_HOME/include -L$JAVA_HOME/lib -shared swiggy_wrap.c -o libswiggy.dylib
    $ javac src/swiggy/Swiggy*.java
    $ export JAVA_OPTS="-Djava.library.path=. -verbose:jni"
    $ lein repl
    ... snipped tons of irrelevant -verbose:jni output ...
    user=> (System/loadLibrary "swiggy")
    nil
    user=> (swiggy.Swiggy/get_something)
    java.lang.UnsatisfiedLinkError: swiggy.SwiggyJNI.get_something()I (NO_SOURCE_FILE:0)

That’s interesting. libswiggy.dylib loaded happily and yet there’s an UnsatisfiedLinkError. The thing to note here is that it’s complaining about linking the get_something() method, not the library itself. That’s unexpected. Also note that I have the -verbose:jni flag set, but nothing was printed about linking the method to the backing native code in the library. hmmm.

So, I googled. And found a couple references to this problem without real solutions. Unfortunately, the usual reaction to this problem is immediately “Did you set LD_LIBRARY_PATH and java.library.path?” The exception is the same and that’s normally the problem in Java. It turns out that putting the loadLibrary() call within a Java class that’s loaded by Clojure is enough to trick the system into correctly linking the native method. That is, edit Swiggy.java something like this:

    ...
    public class Swiggy {
      // Explicitly load the library when this class loads
      static {
        System.loadLibrary("swiggy");
      }
      public static int get_something() {
        return SwiggyJNI.get_something();
      }
    }
    ...

then recompile and give it another try:

    $ javac src/swiggy/Swiggy*.java
    $ lein repl
    user=> (swiggy.Swiggy/get_something)
    [Dynamic-linking native method swiggy.SwiggyJNI.get_something ... JNI]
    42

That’s better. Does anyone know what causes this? Is it related to the classloader used by the repl or something?

If your in the situation where you can’t or don’t want to modify the swig interface file to include this change, all is not lost. Simply create your own dummy Java class with the loadLibrary call and reference it from Clojure. You can even use Leiningen’s :java-source-path property to get it to auto-compile it for you. I suppose gen-class should also work, but the Java’s pretty trivial.

Categories: clojure Tags: , , ,
  1. Aaron Cohen
    September 17th, 2011 at 22:44 | #1

    This is the same problem as I diagnosed here: https://groups.google.com/forum/#!topic/clojure-dev/awe7-yeieIM

    I never heard any response from people. I outline another workaround if you want to stick to clojure code only here:https://groups.google.com/d/msg/clojure/br_sTSuWBJ8/NXppL1EWZhAJ

    It’d be great if you could post to the mailing list and show that other people also have experienced this problem.

  2. Maks Romih
    December 30th, 2012 at 14:39 | #2

    Thanks for publishing and clear explanation.

  3. Kocki
    January 2nd, 2014 at 05:09 | #3

    From Clojure 1.4 on, there is a function clojure.lang.RT/loadLibrary that works.
    http://dev.clojure.org/jira/browse/CLJ-843

  1. No trackbacks yet.