Libloading fails to open a library saying it doesn't exist when it does

I have some code that's trying to load in two DLLs or sos from the same directory.
The first loads fine but the second fails with:
thread 'main' panicked at 'Failed to find IM library: DlOpen { desc: "libiup.so: cannot open shared object file: No such file or directory" }'
Yet the looked-for library does exist and the path I give is correct, so the error message is wrong.

I had exactly the same problem with Python. However, in Python I found a solution:

DLL_ROOT = (pathlib.Path(__file__).parent / 'iup').resolve()
# Windows & error checking code omitted
DLL_ROOT /= 'linux'
iup_dll = DLL_ROOT / 'libiup.so'
iup = ctypes.cdll.LoadLibrary(iup_dll) # Equivalent to libloading::Library::new()
im_dll = 'iupim' # only the bare name needed in this case
im = ctypes.CDLL(im_dll, handle=iup) #### Is there a libloading equivalent to this?

And this works. Notice that for the first .so I load conventionally, but for the second I give the first .so as a "handle" to be used when loading the second library.

Can the equivalent be done using Rust?

When you run your program with strace, which syscall is return the ENOENT?

Here's an extract from strace's output:

openat(AT_FDCWD, "/usr/lib/x86_64/libiup.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7ffd115eb150) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/libiup.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)

So ISTM that when I try to load libiupim.so inside that shared library it searches system-wide for libiup.so without considering its own directory. This is no use to me because I want to be able to create self-contained distributable applications (exe + shared libs) without touching system-wide libraries.

Yet when I used Python, providing I give the original libiup.so's "handle" when opening the im library, it all just works. So, maybe in such cases Python looks in the same directory as the "handle" it is passed? Anyway, libloading doesn't appear to have such a feature, so I've hit a wall with this.

On Linux, normally only system paths are searched. You can adjust the search paths by setting LD_LIBRARY_PATH environment variable or the DT_RPATH/DT_RUNPATH in your shared object. See man dlopen for details.

I don't know how the handle stuff in Python works.

Try doing something along the lines of:

fn iup_dll () -> ::std::io::Result<PathBuf>
{Ok({
    let __file__ = ::std::env::current_exe()?;
    let mut dll_root = __file__.parent()?.join("iup").canonicalize()?;
    dll_root.push("linux");
    // Windows & error checking code omitted
    dll_root.join("libiup.so")
})}
let iup_dll = iup_dll().unwrap();
let lib = ::libloading::Library::new(iup_dll).unwrap();

I already give it absolute paths:

lazy_static! {
    pub(crate) static ref IUP_LIB: Library = Library::new(iup_dll()).expect(
        "Failed to find IUP library");
    pub static ref IUP: Iup<'static> = Iup::new().expect(
        "Failed to create IUP object");
}

fn iup_dll() -> PathBuf {
    exe_path().join(if cfg!(windows) { "iup.dll" } else { "libiup.so" })
}

fn exe_path() -> PathBuf {
    let exe = env::current_exe().expect("Failed to find exe's path");
    let root = exe.parent().expect(
        "Failed to find location of IUP library");
    let mut root = root.to_path_buf();
    if cfg!(windows) {
        root.push("iup/windows");
    } else {
        root.push("iup/linux");
    }
    root
}

The above works fine. But as soon as I add the im library it fails because that library (even though I pass it with a full path) when it opens searches for the iup library system-wide and of course fails. What Python's ctypes module is able to do is work around this, but I don't know how it does it.

Here's the code that doesn't work:

lazy_static! {
    pub(crate) static ref IUP_LIB: Library = Library::new(iup_dll()).expect(
        "Failed to find IUP library");
    pub(crate) static ref IM_LIB: Library = Library::new(im_dll()).expect(
        "Failed to find IM library");
    pub static ref IUP: Iup<'static> = Iup::new().expect(
        "Failed to create IUP object");
    pub static ref IM: Im<'static> = Im::new();
}

fn iup_dll() -> PathBuf {
    exe_path().join(if cfg!(windows) { "iup.dll" } else { "libiup.so" })
}

fn im_dll() -> PathBuf {
    exe_path().join(if cfg!(windows) { "iupim.dll" } else { "libiupim.so" })
}

fn exe_path() -> PathBuf {
    let exe = env::current_exe().expect("Failed to find exe's path");
    let root = exe.parent().expect(
        "Failed to find location of IUP library");
    let mut root = root.to_path_buf();
    if cfg!(windows) {
        root.push("iup/windows");
    } else {
        root.push("iup/linux");
    }
    root
}

So I already use the abs path for im but that doesn't help since im itself looks for iup system-wide.

My bad, I misread (I thought that strace came from the libloading call, when it actually came from the inner iupim.so being loaded).

Then you should try @jethrogb's suggestion, something along the lines of:

fn with_env<R> (
    key: impl AsRef<::std::ffi::OsStr>,
    value: impl AsRef<::std::ffi::OsStr>,
    f: impl FnOnce() -> R,
) -> R
{
    use ::std::env;
    let key = key.as_ref();
    let prev = env::var_os(key);
    env::set_var(key, value);
    ::scopeguard::defer(if let Some(prev) = prev {
        env::set_var(key, prev);
    });
    f()
}

with_env("LD_LIBRARY_PATH", exe_path().to_str().unwrap(), || {
    // Within this scope, LD_LIBRARY_PATH points to exe_path
    ::lazy_static::initialize(&IM_LIB);
});

You might also try loading the IUP library with RTLD_GLOBAL set (not sure what libloading's default is).

I can't get it to compile (see below). Also I thought LD_LIBRARY_PATH was Linux-specific? I'm hoping to build on both Linux and Windows.
Here's the code that won't compile:

lazy_static! {
    pub(crate) static ref IUP_LIB: Library = Library::new(iup_dll()).expect(
        "Failed to find IUP library");
    pub(crate) static ref IM_LIB: Library = Library::new(im_dll()).expect(
        "Failed to find IM library");
    pub static ref IUP: Iup<'static> = Iup::new().expect(
        "Failed to create IUP object");
    pub static ref IM: Im<'static> = Im::new();
}

fn iup_dll() -> PathBuf {
    exe_path().join(if cfg!(windows) { "iup.dll" } else { "libiup.so" })
}

fn im_dll() -> PathBuf {
    exe_path().join(if cfg!(windows) { "iupim.dll" } else { "libiupim.so" })
}

fn exe_path() -> PathBuf {
    let exe = env::current_exe().expect("Failed to find exe's path");
    let root = exe.parent().expect(
        "Failed to find location of IUP library");
    let mut root = root.to_path_buf();
    if cfg!(windows) {
        root.push("iup/windows");
    } else {
        root.push("iup/linux");
    }
    root
}

fn with_env<R> (key: impl AsRef<::std::ffi::OsStr>,
                value: impl AsRef<::std::ffi::OsStr>,
                func: impl FnOnce() -> R) -> R {
    let key = key.as_ref();
    let prev = env::var_os(key);
    env::set_var(key, value);
    scopeguard::defer!(if let Some(prev) = prev {
        env::set_var(key, prev);
    });
    func()
}

with_env("LD_LIBRARY_PATH", exe_path().to_str().unwrap(), || {
    lazy_static::initialize(&IM_LIB);
});
lazy_static::initialize(&IM);

And here's the error (same on Linux and Windows):

error: expected one of `!` or `::`, found `(`
  --> /home/mark/app/rs/iup/src/iup.rs:57:9
   |
57 | with_env("LD_LIBRARY_PATH", exe_path().to_str().unwrap(), || {
   |         ^ expected one of `!` or `::`

I'm very puzzled since with_env() is a function not a macro.

Well, given that the 100% portable approach of using libloading doesn't work, we'll need to tinker a bit. The error you showed seemed to be happening on a Unix system, so let's try to tackled that. Later we can cfg the platform specific tweaks to get something more robust.

  • I am suggesting using an env var due to its "simplicity", but maybe we'll have to be calling dlopen directly, to provide specific flags such as the one @jethrogb mentioned (see the man of dlopen for more info).

In the meantime, you can also try to see if it works on Windows. (With no tweaks, I mean).


Regarding the error message, I wasn't very clear in my suggested code: the with_env call must be inside a function (to call the initialization logic), such as main(), or your own custom init() function that you can mark #[ctor], or within the constructor of the IM_LIB lazy static:

static ref IM_LIB: Library = with_env(..., || {
    Library::new(...)
});

I tried that:

    pub(crate) static ref IM_LIB: Library =
        with_env("LD_LIBRARY_PATH", exe_path().to_str().unwrap(), || {
            Library::new(im_dll()).expect("Failed to find IM library")
        });

(All else as before).
But it crashed for the same reason:

    Running `target/release/helloiup`
thread 'main' panicked at 'Failed to find IM library: DlOpen { desc: "libiup.so: cannot open shared object file: No such file or directory" }', /home/mark/app/rs/iup/src/iup.rs:19:13

BTW regarding dlopen, it doesn't appear to be in the std. lib.?

It is possible that the dynamic linker only reads LD_LIBRARY_PATH during startup. I think Library::new does accept absolute paths to dynamic libraries, so you could try that.

All the paths I give to libloading are absolute but it doesn't make any difference.
This is because when the IM library is loaded it internally attempts to load the IUP library and does so by looking for it in system-wide locations.
Somehow Python manages to avoid this problem by opening one library with a reference to another which "just works" (but how or why I don't know). But libloading doesn't seem to provide an equivalent functionality.

Can you post the output of

ldd libiup.so

and

ldd libiupim.so

to see what they actually depend on and try to load. It might be that one of them has an absolute import which doesn't exist in your filesystem.

ldd libiup.so:

	linux-vdso.so.1 (0x00007ffc37fd6000)
	libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f8e561c6000)
	libgdk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgdk-3.so.0 (0x00007f8e55ed0000)
	libpangocairo-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x00007f8e55cc3000)
	libcairo.so.2 => /usr/lib/x86_64-linux-gnu/libcairo.so.2 (0x00007f8e559a6000)
	libgdk_pixbuf-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 (0x00007f8e55782000)
	libpango-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x00007f8e55535000)
	libgobject-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 (0x00007f8e552e1000)
	libglib-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f8e54fca000)
	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f8e54c92000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8e548f4000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8e54503000)
	libgmodule-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 (0x00007f8e542ff000)
	libXi.so.6 => /usr/lib/x86_64-linux-gnu/libXi.so.6 (0x00007f8e540ef000)
	libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007f8e53ee9000)
	libcairo-gobject.so.2 => /usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2 (0x00007f8e53ce0000)
	libatk-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0 (0x00007f8e53aba000)
	libatk-bridge-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0 (0x00007f8e53889000)
	libepoxy.so.0 => /usr/lib/x86_64-linux-gnu/libepoxy.so.0 (0x00007f8e53588000)
	libpangoft2-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 (0x00007f8e53372000)
	libfontconfig.so.1 => /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f8e5312d000)
	libgio-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0 (0x00007f8e52d8e000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8e52b6f000)
	libXinerama.so.1 => /usr/lib/x86_64-linux-gnu/libXinerama.so.1 (0x00007f8e5296c000)
	libXrandr.so.2 => /usr/lib/x86_64-linux-gnu/libXrandr.so.2 (0x00007f8e52761000)
	libXcursor.so.1 => /usr/lib/x86_64-linux-gnu/libXcursor.so.1 (0x00007f8e52557000)
	libXcomposite.so.1 => /usr/lib/x86_64-linux-gnu/libXcomposite.so.1 (0x00007f8e52354000)
	libXdamage.so.1 => /usr/lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007f8e52151000)
	libxkbcommon.so.0 => /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f8e51f12000)
	libwayland-cursor.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-cursor.so.0 (0x00007f8e51d0a000)
	libwayland-egl.so.1 => /usr/lib/x86_64-linux-gnu/libwayland-egl.so.1 (0x00007f8e51b08000)
	libwayland-client.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-client.so.0 (0x00007f8e518f9000)
	libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007f8e516e7000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f8e514df000)
	libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f8e5122b000)
	libpixman-1.so.0 => /usr/lib/x86_64-linux-gnu/libpixman-1.so.0 (0x00007f8e50f86000)
	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f8e50d54000)
	libxcb-shm.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-shm.so.0 (0x00007f8e50b51000)
	libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f8e50929000)
	libxcb-render.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-render.so.0 (0x00007f8e5071c000)
	libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007f8e50512000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f8e502f5000)
	libthai.so.0 => /usr/lib/x86_64-linux-gnu/libthai.so.0 (0x00007f8e500ec000)
	libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f8e4fee4000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f8e4fc72000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f8e4fa6e000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8e56ace000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f8e4f821000)
	libatspi.so.0 => /usr/lib/x86_64-linux-gnu/libatspi.so.0 (0x00007f8e4f5f1000)
	libharfbuzz.so.0 => /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0 (0x00007f8e4f353000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f8e4f121000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f8e4eef9000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f8e4ecde000)
	libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x00007f8e4ea8a000)
	libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f8e4e886000)
	libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f8e4e680000)
	libdatrie.so.1 => /usr/lib/x86_64-linux-gnu/libdatrie.so.1 (0x00007f8e4e479000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f8e4e1f5000)
	libgraphite2.so.3 => /usr/lib/x86_64-linux-gnu/libgraphite2.so.3 (0x00007f8e4dfc8000)
	libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x00007f8e4dd7b000)
	libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f8e4db66000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f8e4d940000)
	liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f8e4d724000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f8e4d408000)
	libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f8e4d201000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f8e4cfec000)

ldd libiupim.so:

	linux-vdso.so.1 (0x00007ffe3c5e0000)
	libiup.so => not found
	libim.so => not found
	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f321caf1000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f321c700000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f321c4e3000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f321c145000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f321cd23000)

ls -lh

-rwxrwxr-x 1 mark mark  33K Jul 15 08:26 libiupcd.so
-rwxrwxr-x 1 mark mark 329K Jul 15 08:26 libiupcontrols.so
-rwxrwxr-x 1 mark mark 133K Jul 15 08:26 libiupglcontrols.so
-rwxrwxr-x 1 mark mark  27K Jul 15 08:26 libiupgl.so
-rwxrwxr-x 1 mark mark 265K Jul 15 08:26 libiupimglib.so
-rwxrwxr-x 1 mark mark  27K Jul 15 08:26 libiupim.so
-rwxrwxr-x 1 mark mark 3.3M Jul 15 08:26 libiup_mglplot.so
-rwxrwxr-x 1 mark mark 299K Jul 15 08:26 libiup_plot.so
-rwxrwxr-x 1 mark mark 4.2M Jul 15 08:26 libiup_scintilla.so
-rwxrwxr-x 1 mark mark 1.3M Jul 15 08:26 libiup.so
-rwxrwxr-x 1 mark mark 184K Jul 15 08:26 libiuptuio.so
-rwxrwxr-x 1 mark mark  29K Jul 15 08:26 libiupweb.so

The good news is that until ldd starts working, you don't need to deal with Python or Rust. :slight_smile:

Can you do

LD_LIBRARY_PATH="." ldd libiupim.so

That should make libiup.so visible. Not sure about libim.so where it's coming from -- do you have it in another directory?

Edit: Do you need to load those libraries dynamically? Unlike Python, in Rust you could link your executable directly with them, then you wouldn't need to use libloading. You can see an example here (not Rust but the principle is the same.)

I will have to find libim.so; I thought I had all the shared libs I needed!
LD_LIBRARY_PATH="." ldd libiupim.so

	linux-vdso.so.1 (0x00007ffe8673c000)
	libiup.so => ./libiup.so (0x00007f4e3bce0000)
	libim.so => not found
	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f4e3ba0d000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4e3b61c000)
	libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f4e3ad14000)
	libgdk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgdk-3.so.0 (0x00007f4e3aa1e000)
	libpangocairo-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x00007f4e3a811000)
	libcairo.so.2 => /usr/lib/x86_64-linux-gnu/libcairo.so.2 (0x00007f4e3a4f4000)
	libgdk_pixbuf-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 (0x00007f4e3a2d0000)
	libpango-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x00007f4e3a083000)
	libgobject-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 (0x00007f4e39e2f000)
	libglib-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f4e39b18000)
	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f4e397e0000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4e39442000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f4e39225000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4e3bc3f000)
	libgmodule-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 (0x00007f4e39021000)
	libXi.so.6 => /usr/lib/x86_64-linux-gnu/libXi.so.6 (0x00007f4e38e11000)
	libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007f4e38c0b000)
	libcairo-gobject.so.2 => /usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2 (0x00007f4e38a02000)
	libatk-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0 (0x00007f4e387dc000)
	libatk-bridge-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0 (0x00007f4e385ab000)
	libepoxy.so.0 => /usr/lib/x86_64-linux-gnu/libepoxy.so.0 (0x00007f4e382aa000)
	libpangoft2-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 (0x00007f4e38094000)
	libfontconfig.so.1 => /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f4e37e4f000)
	libgio-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0 (0x00007f4e37ab0000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f4e37891000)
	libXinerama.so.1 => /usr/lib/x86_64-linux-gnu/libXinerama.so.1 (0x00007f4e3768e000)
	libXrandr.so.2 => /usr/lib/x86_64-linux-gnu/libXrandr.so.2 (0x00007f4e37483000)
	libXcursor.so.1 => /usr/lib/x86_64-linux-gnu/libXcursor.so.1 (0x00007f4e37279000)
	libXcomposite.so.1 => /usr/lib/x86_64-linux-gnu/libXcomposite.so.1 (0x00007f4e37076000)
	libXdamage.so.1 => /usr/lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007f4e36e73000)
	libxkbcommon.so.0 => /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f4e36c34000)
	libwayland-cursor.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-cursor.so.0 (0x00007f4e36a2c000)
	libwayland-egl.so.1 => /usr/lib/x86_64-linux-gnu/libwayland-egl.so.1 (0x00007f4e3682a000)
	libwayland-client.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-client.so.0 (0x00007f4e3661b000)
	libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007f4e36409000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f4e36201000)
	libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f4e35f4d000)
	libpixman-1.so.0 => /usr/lib/x86_64-linux-gnu/libpixman-1.so.0 (0x00007f4e35ca8000)
	libxcb-shm.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-shm.so.0 (0x00007f4e35aa5000)
	libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f4e3587d000)
	libxcb-render.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-render.so.0 (0x00007f4e35670000)
	libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007f4e35466000)
	libthai.so.0 => /usr/lib/x86_64-linux-gnu/libthai.so.0 (0x00007f4e3525d000)
	libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f4e35055000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f4e34de3000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4e34bdf000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f4e34992000)
	libatspi.so.0 => /usr/lib/x86_64-linux-gnu/libatspi.so.0 (0x00007f4e34762000)
	libharfbuzz.so.0 => /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0 (0x00007f4e344c4000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f4e34292000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f4e3406a000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f4e33e4f000)
	libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x00007f4e33bfb000)
	libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f4e339f7000)
	libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f4e337f1000)
	libdatrie.so.1 => /usr/lib/x86_64-linux-gnu/libdatrie.so.1 (0x00007f4e335ea000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f4e33366000)
	libgraphite2.so.3 => /usr/lib/x86_64-linux-gnu/libgraphite2.so.3 (0x00007f4e33139000)
	libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x00007f4e32eec000)
	libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f4e32cd7000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f4e32ab1000)
	liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f4e32895000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f4e32579000)
	libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f4e32372000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f4e3215d000)

I specifically want to use dynamic loading. The reason being that I can then distribute an executable plus .sos or .dlls without having to build them for a particular target (beyond OS and word size). This is esp. helpful on Windows where I dislike building things.

I'm trying to understand what does Python do when you do

im = ctypes.CDLL(im_dll, handle=iup)

and looking at ctypes/__init__.py it doesn't even call dlopen(). It seems to reuse the handle it got from iup = ctypes.cdll.LoadLibrary(iup_dll).

Wondering whether your Python program would continue to work if you did

im = ctypes.CDLL("xxx", handle=iup)

where xxx is there only to check if the supplied name is used at any point or only the handle matters.

It might be worth to try to understand why does the Python code work (maybe check Python forum?) and only then reproduce it in Rust. Failing that, you can let your users start your program with a shell script that appends . to LD_LIBRARY_PATH to make .so visible to the linker or let the user install your dependencies system-wide using their package manager.

1 Like

Using a shell script might solve it on Linux, but the whole point for me is to be able to create "stand-alone" cross-platform (Linux + Windows) GUI programs.

I've now finally got all the .sos and .dll's (not that it has helped).
For example LD_LIBRARY_PATH="." ldd libiupim.so:

./libiupim.so: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by ./libim.so)
	linux-vdso.so.1 (0x00007ffde0561000)
	libiup.so => ./libiup.so (0x00007f59dc46c000)
	libim.so => ./libim.so (0x00007f59dc25f000)
	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007f59dc02d000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f59dbc3c000)
	libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f59db334000)
	libgdk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgdk-3.so.0 (0x00007f59db03e000)
	libpangocairo-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x00007f59dae31000)
	libcairo.so.2 => /usr/lib/x86_64-linux-gnu/libcairo.so.2 (0x00007f59dab14000)
	libgdk_pixbuf-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 (0x00007f59da8f0000)
	libpango-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x00007f59da6a3000)
	libgobject-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 (0x00007f59da44f000)
	libglib-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f59da138000)
	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f59d9e00000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f59d9a62000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f59d9845000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f59d94bc000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f59d92a4000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f59dc3cb000)
	libgmodule-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 (0x00007f59d90a0000)
	libXi.so.6 => /usr/lib/x86_64-linux-gnu/libXi.so.6 (0x00007f59d8e90000)
	libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007f59d8c8a000)
	libcairo-gobject.so.2 => /usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2 (0x00007f59d8a81000)
	libatk-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0 (0x00007f59d885b000)
	libatk-bridge-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0 (0x00007f59d862a000)
	libepoxy.so.0 => /usr/lib/x86_64-linux-gnu/libepoxy.so.0 (0x00007f59d8329000)
	libpangoft2-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 (0x00007f59d8113000)
	libfontconfig.so.1 => /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007f59d7ece000)
	libgio-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0 (0x00007f59d7b2f000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f59d7910000)
	libXinerama.so.1 => /usr/lib/x86_64-linux-gnu/libXinerama.so.1 (0x00007f59d770d000)
	libXrandr.so.2 => /usr/lib/x86_64-linux-gnu/libXrandr.so.2 (0x00007f59d7502000)
	libXcursor.so.1 => /usr/lib/x86_64-linux-gnu/libXcursor.so.1 (0x00007f59d72f8000)
	libXcomposite.so.1 => /usr/lib/x86_64-linux-gnu/libXcomposite.so.1 (0x00007f59d70f5000)
	libXdamage.so.1 => /usr/lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007f59d6ef2000)
	libxkbcommon.so.0 => /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007f59d6cb3000)
	libwayland-cursor.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-cursor.so.0 (0x00007f59d6aab000)
	libwayland-egl.so.1 => /usr/lib/x86_64-linux-gnu/libwayland-egl.so.1 (0x00007f59d68a9000)
	libwayland-client.so.0 => /usr/lib/x86_64-linux-gnu/libwayland-client.so.0 (0x00007f59d669a000)
	libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007f59d6488000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f59d6280000)
	libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f59d5fcc000)
	libpixman-1.so.0 => /usr/lib/x86_64-linux-gnu/libpixman-1.so.0 (0x00007f59d5d27000)
	libxcb-shm.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-shm.so.0 (0x00007f59d5b24000)
	libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f59d58fc000)
	libxcb-render.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-render.so.0 (0x00007f59d56ef000)
	libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007f59d54e5000)
	libthai.so.0 => /usr/lib/x86_64-linux-gnu/libthai.so.0 (0x00007f59d52dc000)
	libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f59d50d4000)
	libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f59d4e62000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f59d4c5e000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f59d4a11000)
	libatspi.so.0 => /usr/lib/x86_64-linux-gnu/libatspi.so.0 (0x00007f59d47e1000)
	libharfbuzz.so.0 => /usr/lib/x86_64-linux-gnu/libharfbuzz.so.0 (0x00007f59d4543000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f59d4311000)
	libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f59d40e9000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f59d3ece000)
	libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x00007f59d3c7a000)
	libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f59d3a76000)
	libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f59d3870000)
	libdatrie.so.1 => /usr/lib/x86_64-linux-gnu/libdatrie.so.1 (0x00007f59d3669000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f59d33e5000)
	libgraphite2.so.3 => /usr/lib/x86_64-linux-gnu/libgraphite2.so.3 (0x00007f59d31b8000)
	libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x00007f59d2f6b000)
	libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f59d2d56000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f59d2b30000)
	liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f59d2914000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f59d25f8000)
	libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f59d23f1000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f59d21dc000)

As you can see, it appears that no matter what it searches for system-wide libraries.

Anyway, I think I'll give up on this and look for another way to do the cross-platform GUI programming I want.