FFI linking two libraries with same function signatures (ImageMagick/GraphicsMagick)

Hi, I am refactoring a command-line app that use both ImageMagick and GraphicsMagick. Previous, it shelled out to terminal to call those, now I want to use FFI. However, I am running into issue linking them because both ImageMagick and GraphicsMagick have pretty much the same C API surface.

My question is is it possible to link to both of them from them same Rust app?

My app is setup to use pkg-config, which did not work, as I cannot specify which library to link to for each of my extern declarations. I also tried to use #[link(name = "libname")] and that appears to have yielded the same result.

Thanks

What about this approach:

mod ImageMagick {
    #[link(name = "lib_imgmagick")]
    extern {
        //Your fns here
    }
}
mod GraphicsMagick {
    #[link(name = "lib_gfxmagick")]
    extern {
        //Your fns here
    }
}

(Note that I don't know the actual name for the libraries that you'd have to put into #[link])


You may also be interested in the links entry in the manifest

I don't think it's possible to have both linked into one executable at the same time, regardless of what you do in Rust.

If either of the libraries doesn't have an option to prefix all of its symbols with something unique, their symbols will collide. That's a C/linker issue, and Rust can't do anything about that.

C has a concept of hidden symbols, but AFAIK you need to actually perform linking to hide the symbols, so that's not useful for a single executable with two colliding libraries. You might try wrapping one of the libraries in a dynamic library, that contains *Magick as a fully static library, with all of its symbols marked as hidden, and only exporting publicly a custom C API that does what you want, and then link that with your program.

5 Likes

That's a bummer.

I managed to get the code to compile and not segfault randomly by moving the FFI code to different crates. This works fine for unit tests, but during actual runtime only one of the libraries will be correctly loaded (I can verify this because the font catalog of each library is different, but during runtime, they both show the same font catalog).

Do you know if can get around this problem by using fork and "dynamic loading". (i.e. pre-fork neither libs will be loaded, post-fork load the right one)

I'd normally solve this with dynamic loading. The libloading crate is an excellent cross-platform library for doing this sort of stuff.

It can be annoying having to load every single function pointer you'll need from the library instead of having the linker/loader do this for you, but that's arguably a good reason to try and keep interfaces small.

Another option might be running objcopy on the obj files of one (or both) of those libs and ask it to prefix the symbols. Then use the #[link(...)] directive that @OptimisticPeach mentioned.

2 Likes