So I'm writing the Rust library (libExt) that must be able to reference symbols (functions) from the other already loaded by the executable C library (libBase). You can think of it as an "extension", "plugin", etc. library.
Few things:
if libBase is not loaded - crash or whatever, this is UB, don't care
libBase can be both - statically and dynamically loaded in the main executable (in the worst case only dynamic library can be considered)
libBase and libExt are both loaded by the executable, neither library should load any other
I know about "function pointer tables/callbacks during initialisation/registration/loading" and need to avoid this at any cost. libBase is really huge and I need to be able to access any function from it
Try #1
// libBase - just an example, this is actually a C code and all functions are loadable by the main program
pub extern "C" fn already_loaded_symbol() {
// do something
}
This way it at least works but it also requires libBase to be available during the build.
Questions
I would like an ability to write libExt without having a build dependency on the libBase and without writing all dlsym calls myself. What I had in mind is something like automatic dlopen(null)+dlsym by the extern block.
Is this even possible or maybe there are some nice workarounds / best practices taking to account limitation #4?
@steffahn, thanks for the reply. As far as I know - extern "C" automatically implies no_mangle. In any case libBase is a C library, I've just written it as an example.
What you are asking for depends not on properties of Rust or C languages but on properties of linkers (static linkers if we are talking about static libraries, dynamic linking if we are talking about dynamic libraries).
In particular it should work fine with GNU/Linux (where lazy loading is expected and supported) and shouldn't work with bionic (Android's libc) — by design: Android loader only looks into libraries which are dependencies of your library and doesn't ever try to look into other libraries.
This was done specifically to prevent what you expect to happen — this way system libraries can introduce any symbols they want without them automatically polluting namespace of user-provided libraries.
The extern "C" is for specifying a call convention, and there are legitimate places where you might want a mangled symbol with the C calling convention (e.g. callbacks) or an unmangled symbol using the default Rust calling convention (e.g. the hooks Rust uses for accessing the global allocator).
You need to add #[no_mangle] to your function.
Edit: ignore me. I was referring to extern "C" as applied to function definitions (e.g. your already_loaded_symbol() in libBase), while you were asking about the extern "C" blockin libExt. You don't need to specify #[no_mangle] in libExt.
Yeah, you need to specify #[no_mangle] when defining a function, but extern blocks work by looking for symbols using the name you use in the function declaration.
In "try 1" it looked like they had forgotten a #[no_mangle] when defining already_loaded_symbol() in libBase, which would normally cause this "symbol not found" error.
Do you know what flags are being used with dlopen? If libBase was loaded with RTLD_LOCAL, not RTLD_GLOBAL, then its symbols won't be available when libExt is loaded. This might be what was said here:
hey. as I wrote - libBase is just an example. it is actually written in C and functions can be loaded manually without any problem. There is no problem with mangling and nm also shows functions without mangling. The question is solely about libExt.
If it's the disk space hugeness of libBase that is a problem, I'm pretty sure there are utilities to generate a dummy .so file with just the symbol list, no actual function bodies.