1

I have a Rust project, set as an executable. I am trying to call an external shared library, also written in Rust, dynamically. I have the external library compiled on release, and I have tried both crate types cdylib and dylib.

I am using the crate libloading, which claims to be able to dynamically load shared library functions, as long as they only use primitive arguments. I keep getting this error when I try to run my code using this crate.

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: GetProcAddress { source: Os { code: 127, kind: Other, message: "The specified procedure could not be found." } }', src\main.rs:14:68
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

main.rs/main():

    let now = Instant::now();

    unsafe {
        let lib = libloading::Library::new(
            "externtest.dll").unwrap();
        let foo3: Symbol<extern fn(i32) -> i32> = lib.get(b"foo").unwrap();

        println!("{}", foo(1));
    }

    let elapsed = now.elapsed();
    println!("Elapsed: {:?}", elapsed);

lib.rs:

pub extern "C" fn foo3(i:i32) -> i32{
    i
}
kmdreko
  • 42,554
  • 6
  • 57
  • 106

1 Answers1

4

First, your library function is called "foo3", but you're trying to load the symbol "foo".

Second, the library function's symbol may not match it's name due to mangling. You need to tell the compiler not to do that with the #[no_mangle] attribute:

#[no_mangle]
pub extern "C" fn foo(i: i32) -> i32 {
    i
}

Third, this is mostly a stylistic choice, but I'd specify the ABI extern "C" when defining your symbol. Even though extern with no epecified ABI uses the "C" ABI, I've found it better to be explicit.

use libloading::{Library, Symbol};

fn main() {
    unsafe {
        let lib = Library::new("externtest.dll").unwrap();
        let foo = lib
            .get::<Symbol<extern "C" fn(i32) -> i32>>(b"foo")
            .unwrap();

        println!("{}", foo(1));
    }
}

The above should work without issue.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Thank you! For the problems with the function name, that is because with all the things I tried to get it working, I messed some stuff up. Also, I was sure that by using extern, you did not have to use #[no_mangle], but I guess I was wrong. I thought I had read that in an accepted pull request on Rust's GitHub. –  Feb 18 '21 at 04:20
  • @Duel I believe `pub extern "C"` implying `#[no_mangle]` has been *proposed*, but I don't think it had actually been changed. Of course, let me know if you find a PR to the contrary. – kmdreko Feb 18 '21 at 04:28
  • 1
    @Duel they're currently two separate concerns: `extern fn` is about the ABI, and `no_mangle` is about the symbol's name. You may have a use for one without the other e.g. if you need a C-compatible callback but will not be exposing the symbol, keeping it mangled is probably safer. It *is* a bit confusing because `extern {}` *blocks* use the names as-is. There have been a proposals to e.g. make `pub extern fn` implicitly unmangled (and keep `extern fn` mangled), but they're breaking changes so.... – Masklinn Feb 18 '21 at 07:00
  • 1
    (it's also confusing because C++'s `extern "C"` is Rust's `no_mangle`) – Masklinn Feb 18 '21 at 07:03
  • Yeah, I see why they would be hesitant to change that. –  Feb 18 '21 at 20:19