When you obtain a dynamic library from this code
#include "stdint.h"
int32_t add(int32_t a, int32_t b) { return a + b; }
the symbol of this function is not add
but _Z3addii
$ nm -D libfoo.so | grep -F add
00000000000010e9 T _Z3addii
This happens because C++ allows function overloading (i.e. multiple functions with the same name but different parameters) then the name of the function itself is not sufficient to find it in the library (many functions could have the same name).
As an experiment, let's try to invoke the function with its mangled name (bad practice, we will come back to it later).
In main.rs
, we simply replace add
by this mangled name.
extern "C" { fn _Z3addii(x: i32, y: i32) -> i32; }
...
let x = unsafe { _Z3addii(62, 30) };
and this time it works.
$ rustc -l foo -L . main.rs
$ LD_LIBRARY_PATH=. ./main
~~> 92
The problem is that the name-mangling is not standard in the language and may change from one compiler chain to another, so relying on this specific name is a bad practice, since it's not portable.
Moreover, as stated in a comment below, nothing guaranties that the calling conventions used for this C++ function conforms to the C ABI expected from the Rust side (extern "C"
); thus this experimentation just works « by chance ».
The reliable way to find this symbol, is to inform the C++ compiler that we don't want name-mangling for this specific function (as C does, since function overloading is not allowed).
Of course, only one function with this specific name (add
) can have this restriction, otherwise there would be a collision on the symbol name.
#include "stdint.h"
extern "C" int32_t add(int32_t a, int32_t b) { return a + b; }
This time the symbol is exactly the function name
$ nm -D libfoo.so | grep -F add
00000000000010e9 T add
and this functions conforms to the C ABI in order to be invoked.
Then main.rs
can be restored to
extern "C" { fn add(x: i32, y: i32) -> i32; }
...
let x = unsafe { add(62, 30) };
and everything works as expected
$ rustc -l foo -L . main.rs
$ LD_LIBRARY_PATH=. ./main
~~> 92
Note that our binary has to find at run-time the dynamic library it relies on.
This is why the LD_LIBRARY_PATH
environment variable is used.
On other operating systems, this could change: DYLD_LIBRARY_PATH
on MacOS, PATH
on Windows (or implicitly found in the current directory).
And the name of the library can also change depending on the system: libfoo.dylib
on MacOS, foo.dll
on Windows.
Note also that there exists some tools in order to ease such things.