Dynamically link a DLL using the GNU Toolchain on Windows

Hi, I'm limited to the x86_64-pc-windows-gnu toolchain and am trying to use it to include a DLL dynamically.

to test this, I've build a simple DLL using the same toolchain using this code:

#[unsafe(no_mangle)]
pub extern fn add(left: u64, right: u64) -> u64 {
    left + right
}

now if I try to link to this DLL in another crate, the linker can't find this DLL, and I'm not sure why.

#[link(name = "dll-lib", kind="dylib")]
unsafe extern "C" {
    fn add(left: usize, right: usize) -> usize;
}

fn main() {
    unsafe {
        println!("2+2={}", add(2, 2));
    }
}

This fails to build using cargo with the following:

C:\Users\vboxuser\Documents\rust\dll-runner> cargo build
warning: The package `dll_lib` provides no linkable target. The compiler might raise an error while compiling `dll_runner`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `dll_lib`'s Cargo.toml. This warning might turn into a hard error in the future.
   Compiling dll-runner v0.1.0 (C:\Users\vboxuser\Documents\rust\dll-runner)
error: linking with `x86_64-w64-mingw32-gcc` failed: exit code: 1
  |
  = note: "x86_64-w64-mingw32-gcc" "-fno-use-linker-plugin" "-Wl,--dynamicbase" "-Wl,--disable-auto-image-base" "-m64" "-Wl,--high-entropy-va" "C:\\Users\\vboxuser\\.rustup\\toolchains\\stable-x86_64-pc-windows-gnu\\lib\\rustlib\\x86_64-pc-windows-gnu\\lib\\self-contained\\crt2.o" "C:\\Users\\vboxuser\\.rustup\\toolchains\\stable-x86_64-pc-windows-gnu\\lib\\rustlib\\x86_64-pc-windows-gnu\\lib\\rsbegin.o" "C:\\Users\\vboxuser\\AppData\\Local\\Temp\\rustclN7nLG\\symbols.o" "<8 object files omitted>" "-Wl,-Bdynamic" "-ldll-lib" "-Wl,-Bstatic" "C:\\Users\\vboxuser\\.rustup\\toolchains\\stable-x86_64-pc-windows-gnu\\lib\\rustlib\\x86_64-pc-windows-gnu\\lib/{libstd-e4b8ea78261a0674.rlib,libpanic_unwind-18f69fdf12e459e8.rlib,libobject-b82836e015132809.rlib,libmemchr-dfcc8db2f5996a68.rlib,libaddr2line-32d599866b048d42.rlib,libgimli-d8481f85b019c1a1.rlib,libwindows_targets-c40387f640b865da.rlib,librustc_demangle-f674e29a379618ed.rlib,libstd_detect-80ce46707629d450.rlib,libhashbrown-153796924303d5c9.rlib,librustc_std_workspace_alloc-82a8e1cd44a7f9b6.rlib,libminiz_oxide-7e7db097699d1199.rlib,libadler-64ea0f8a10b8885c.rlib,libunwind-2f74d6e572799413.rlib,libcfg_if-579ef03944d45df1.rlib,liblibc-c1d7ad34196e99e6.rlib,liballoc-499b3583c060fc1b.rlib,librustc_std_workspace_core-0fce1f07cbe774db.rlib,libcore-3c456325284eb8ac.rlib,libcompiler_builtins-2c5e652ae29bab18.rlib}" "-Wl,-Bdynamic" "-lkernel32" "-lkernel32" "-ladvapi32" "-lntdll" "-luserenv" "-lws2_32" "-ldbghelp" "-lgcc_eh" "-l:libpthread.a" "-lmsvcrt" "-lmingwex" "-lmingw32" "-lgcc" "-lmsvcrt" "-lmingwex" "-luser32" "-lkernel32" "-Wl,--nxcompat" "-nostartfiles" "-L" "C:\\Users\\vboxuser\\Documents\\rust\\dll-lib\\target\\debug" "-L" "C:\\Users\\vboxuser\\.rustup\\toolchains\\stable-x86_64-pc-windows-gnu\\lib\\rustlib\\x86_64-pc-windows-gnu\\lib\\self-contained" "-o" "C:\\Users\\vboxuser\\Documents\\rust\\dll-runner\\target\\debug\\deps\\dll_runner-108abb990ef710a1.exe" "-Wl,--gc-sections" "-no-pie" "-nodefaultlibs" "C:\\Users\\vboxuser\\.rustup\\toolchains\\stable-x86_64-pc-windows-gnu\\lib\\rustlib\\x86_64-pc-windows-gnu\\lib\\rsend.o"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: ld: cannot find -ldll-lib: No such file or directory␍


error: could not compile `dll-runner` (bin "dll-runner") due to 1 previous error

I tried using a build script to correct the paths:

fn main(){
	let path = r#"C:\Users\vboxuser\Documents\rust\dll-lib\target\debug"#;
	println!("cargo::rustc-link-search={path}");
}

but this does not help, I still get the "no such file or directory" error.
The error appears with or without the .dllextension in the link attribute. Copying the *.dll or the *.dll.a file to various places where gcc might be looking for it apparently also has no effect.

The linking however works fine using the same directory structure on the MSVC toolchain.
Any Ideas?

The warning text is:

warning: The package dll_lib provides no linkable target. The compiler might raise an error while compiling dll_runner. Consider adding 'dylib' or 'rlib' to key crate-type in dll_lib's Cargo.toml. This warning might turn into a hard error in the future.

So I assume you are using crate-type=cdylib? If so I believe you don't statically link against those in Rust, though you can dynamically link of course. If you use dylib instead you can link directly to it, though you then should not build and ship the library separately due to the unstable ABI.

It is a bit odd this is how Rust is set up!

Yes exactly, though I need to use this to create a DLL, right?
The warning is also just a warning, not a hard error. I'm more confused about why gcc is not finding the link target.
My goal is actually to create a DLL with rust to be used in another non rust project, but since i already have rust running I thought let's test this DLL in another crate, which does work, but only in the MSVC toolchain, which i'd really like to avoid.

I think it's accidental that it works with MSVC, but since I haven't found the reasoning for this warning it's not clear why yet.

I found a few other questions on similar lines without this warning, they were recommending static libraries or explicit dynamic linking, or crate-type=dylib

how do I use "explicit dynamic linking"?

libloading is the most popular crate for it. It's a bit of a pain to keep in sync, unfortunately.

And just in case someone wants it, here is an awesome tutorial for libloading.

1 Like