0

Is it possible to build a shared library (with g++) that has a dependency on another shared library (in my case RocksDB - librocksdb.so), but whose file name at runtime is different? (e.g. librocksdb1234.so). And if so, how can this be done?

More Context

  1. I'm implementing a custom compaction filter for RocksDB (C++) that I intend to use in my Java application via JNI.

  2. When using RocksDB in Java I need to first invoke RocksDB.loadLibrary(), which will load the RocksDB library. The RocksDB JNI jar comes with multiple RocksDB shared objects (.so files), one for each architecture. When I call this method it will select the correct shared object file in the jar, copy it to a temporary folder, and load it using System.load(...). The copied shared object file is created with a random suffix.

  3. For the development of the custom compaction filter, I first installed RocksDB headers (this is simply a call to a Makefile task in the RocksDB project, that will copy the headers to /usr/local/include/rocksdb), and also built RocksDB as a shared library and installed it in /usr/local/lib (again, this is just a call to a Makefile task that already exists in the project).

  4. To build the shared library with the custom compaction filter, I'm using g++ as follows (for linking): g++ -shared -fPI -Wl,--no-undefined <list-of-object-files> -o libcustom.so -lrocksdb (the installed RocksDB shared library is named librocksdb.so).

  5. When I try to load my shared library with the custom compaction filter on my Java application it is successfully loaded as expected (as long as the librocksdb.so is present).

  6. But since RocksDB JNI already comes with the RocksDB shared object files, what I want is to just use those shared objects, and not the librocksdb.so that I installed for development purposes. So, I basically just delete the librocksdb.so and add the call to RocksDB.loadLibrary() in my Java application. Once I do this, I stop being able to load my shared library due to librocksdb.so.6: cannot open shared object file: No such file or directory.

  7. I validated that the RocksDB shared object (from the jar) was properly loaded using lsof -p <pid>.

  8. From what I understand, this happens because my shared object with the custom compaction filter will have an entry in its header that defines a dependency on a library named rocksdb (confirmed using objdump -x ...). But because the RocksDB shared object loaded by RocksDB.loadLibrary() has a random name, it won't match the needed dependency.

Dynamic Section:
  NEEDED               librocksdb.so.6
...

Side question In point 8. I'm assuming that the dynamic loading will be based on the file name of the shared object. Is this a correct assumption? Or will the dynamic loading be based on the SONAME? (I noticed that the shared objects in the RocksDB JNI jar don't have the SONAME defined).

Luis Alves
  • 1,286
  • 12
  • 32
  • If you just need to run on linux you could probably `LD_PRELOAD` to hook `dlopen` and redirect the underlying call that `System.load` makes to point to the system install of `librocksdb`. This is a kind of ugly way of doing it though. – floomby Dec 26 '21 at 17:54
  • If you use glibc, you can call `dl_iterate_phdr` and find out which shared libraries are loaded. – n. m. could be an AI Dec 26 '21 at 20:34
  • It might be worth looking into `dlopen()` and `dlsym()` (https://man7.org/linux/man-pages/man3/dlopen.3.html), which are functions to manually load shared libraries and retrieve function pointers from them. – olm Dec 26 '21 at 21:25

2 Answers2

1

To build the shared library with the custom compaction filter, I'm using g++ as follows

Note that IF you guarantee that all symbols from librocksdb.so.6 needed by your library are already present before your library is loaded, THEN you don't have to link your shared library with librocksdb at all. Simply remove -lrocksdb from your link command line, and it will load just fine.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Thanks for your answer. I tried to remove the `-lrocksdb` but it now fails loading the library with `undefined symbol: _ZNK7rocksdb12Customizable9GetOptionERKNS_13ConfigOptionsERKSsPSs`. I searched for the symbol in the shared object from the jar with `nm -gD` and it exists. – Luis Alves Dec 27 '21 at 15:20
  • Here's the output from `nm -gD ... | grep _ZNK7rocksdb12Customizable9GetOptionERKNS_13ConfigOptionsERKSsPSs` for the `librocksdb` (first) and from my shared library: `00000000005577e0 T _ZNK7rocksdb12Customizable9GetOptionERKNS_13ConfigOptionsERKSsPSs ` versus `U _ZNK7rocksdb12Customizable9GetOptionERKNS_13ConfigOptionsERKSsPSs `. – Luis Alves Dec 27 '21 at 15:30
  • Also, running: `LD_PRELOAD=/.../librocksdbjni-linux64.so ldd -r libcustom.so` shows no undefined symbols. Where `librocksdbjni-linux64.so` is basically the RocksDB shared library in the jar. – Luis Alves Dec 27 '21 at 16:38
  • It seems that the issue is similar to the one mentioned here: https://stackoverflow.com/questions/5425034/java-load-shared-libraries-with-dependencies. – Luis Alves Dec 27 '21 at 17:25
0

Regarding rocksdb specifically, if you have the rocksdb jni share library that you link at build time in one directory, you can set java.library.path to include the directory, then the library will be loaded with its real filenames instead of a temporary one with random suffix.

Lin_n
  • 189
  • 1
  • 4