3

I'm trying to make a Rust program that statically links against libusb, using the MSVC toolchain, but it blows up at run-time from a missing DLL:

error: process didn't exit successfully: `target\debug\test_libusb.exe` (exit code: 0xc0000135, STATUS_DLL_NOT_FOUND)

I used Dependency Walker to find that the missing DLL is LIBUSB-1.0.DLL. Adding it to the project solves the problem and so does dynamically linking everything, but I would like to avoid this solution.

My starting point was this libusb-sys crate, but since it didn't work I made a simpler similar crate. I got libusb from vcpkg.

libusb-sys/src/lib.rs

extern crate libc;

use libc::c_char;

#[repr(C)]
pub struct libusb_version {
    pub major: u16,
    pub minor: u16,
    pub micro: u16,
    pub nano: u16,
    pub rc: *const c_char,
    pub describe: *const c_char,
}

#[link(name = "libusb-1.0", kind = "static")]
extern "C" {
    pub fn libusb_get_version() -> *const libusb_version;
}

libusb-sys/Cargo.toml

[package]
name = "libusb-sys"
version = "0.1.0"

build = "build.rs"
link = "libusb-1.0"

[dependencies]
libc = "0.2"

libusb-sys/build.rs

fn main() {
    println!("
cargo:rustc-link-lib=static=libusb-1.0
cargo:rustc-link-search=native=C:/vcpkg/packages/libusb_x64-windows/lib")
}

Then I used this on my program's crate:

test_libusb/src/main.rs

extern crate libusb_sys as ffi;

fn main() {
    unsafe {
        let version = ffi::libusb_get_version();

        println!(
            "libusb v{}.{}.{}.{}",
            (*version).major,
            (*version).minor,
            (*version).micro,
            (*version).nano
        );
    }
}

test_libusb/Cargo.toml

[package]
name = "test_libusb"
version = "0.1.0"

[dependencies]
"libusb-sys" = { path = "../libusb-sys" }

Here's the entire compiler output, in case it's helpful:

G:\programming\rust\test_libusb> cargo run --verbose
   Compiling libc v0.2.58
   Compiling libusb-sys v0.1.0 (G:\programming\rust\libusb-sys)
     Running `rustc --crate-name build_script_build C:\Users\slysherz\.cargo\registry\src\github.com-1ecc6299db9ec823\libc-0.2.58\build.rs --color always --crate-type bin --emit=dep-info,link -C debuginfo=2 --cfg "feature=\"default\"" --cfg "feature=\"std\"" -C metadata=f83941a94611be11 -C extra-filename=-f83941a94611be11 --out-dir G:\programming\rust\test_libusb\target\debug\build\libc-f83941a94611be11 -L dependency=G:\programming\rust\test_libusb\target\debug\deps --cap-lints allow`     
     Running `rustc --crate-name build_script_build G:\programming\rust\libusb-sys\build.rs --color always --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=71e53c2aee584c72 -C extra-filename=-71e53c2aee584c72 --out-dir G:\programming\rust\test_libusb\target\debug\build\libusb-sys-71e53c2aee584c72 -C incremental=G:\programming\rust\test_libusb\target\debug\incremental -L dependency=G:\programming\rust\test_libusb\target\debug\deps`     
     Running `G:\programming\rust\test_libusb\target\debug\build\libusb-sys-71e53c2aee584c72\build-script-build`
     Running `G:\programming\rust\test_libusb\target\debug\build\libc-f83941a94611be11\build-script-build`
     Running `rustc --crate-name libc C:\Users\slysherz\.cargo\registry\src\github.com-1ecc6299db9ec823\libc-0.2.58\src\lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 --cfg "feature=\"default\"" --cfg "feature=\"std\"" -C metadata=b67580e06366e753 -C extra-filename=-b67580e06366e753 --out-dir G:\programming\rust\test_libusb\target\debug\deps -L dependency=G:\programming\rust\test_libusb\target\debug\deps --cap-lints allow --cfg libc_priv_mod_use --cfg libc_union --cfg libc_const_size_of --cfg libc_align --cfg libc_core_cvoid --cfg libc_packedN`     
     Running `rustc --crate-name libusb_sys G:\programming\rust\libusb-sys\src\lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=94e8ef72b03c18f5 -C extra-filename=-94e8ef72b03c18f5 --out-dir G:\programming\rust\test_libusb\target\debug\deps -C incremental=G:\programming\rust\test_libusb\target\debug\incremental -L dependency=G:\programming\rust\test_libusb\target\debug\deps --extern libc=G:\programming\rust\test_libusb\target\debug\deps\liblibc-b67580e06366e753.rlib -L native=C:/vcpkg/packages/libusb_x64-windows/lib -l static=libusb-1.0`   
    Compiling test_libusb v0.1.0 (G:\programming\rust\test_libusb)
     Running `rustc --crate-name test_libusb src\main.rs --color always --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=8f2bb1a8f56e2223 -C extra-filename=-8f2bb1a8f56e2223 --out-dir G:\programming\rust\test_libusb\target\debug\deps -C incremental=G:\programming\rust\test_libusb\target\debug\incremental -L dependency=G:\programming\rust\test_libusb\target\debug\deps --extern libusb_sys=G:\programming\rust\test_libusb\target\debug\deps\liblibusb_sys-94e8ef72b03c18f5.rlib -L native=C:/vcpkg/packages/libusb_x64-windows/lib`    
     Finished dev [unoptimized + debuginfo] target(s) in 1.20s
     Running `target\debug\test_libusb.exe`
error: process didn't exit successfully: `target\debug\test_libusb.exe` (exit code: 0xc0000135, STATUS_DLL_NOT_FOUND)

Even after compiling libusb from scratch with VS 2017 I still get a link error:

= note: test_libusb-528eb6acc7e5c681.3w5rrhap2r2yw5if.rcgu.o : error LNK2019: unresolved external symbol libusb_get_version referenced in function _ZN11test_libusb4main17hcb0c36db62706c3bE
          G:\programming\rust\test_libusb\target\debug\deps\test_libusb-528eb6acc7e5c681.exe : fatal error LNK1120: 1 unresolved externals

When I call this function directly from the first crate it works though.

I've started to learn Rust recently, so I'm not sure about how any of this works. Why does Rust try to load a DLL in this situation?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
SlySherZ
  • 1,631
  • 1
  • 16
  • 25
  • I don't think it will affect your problem, but I would not have the `link` attribute in your code. Otherwise the build script and the code can diverge and then be conflicting. – Shepmaster Jun 12 '19 at 14:49
  • You also can just type `version.major` etc. when printing. – Shepmaster Jun 12 '19 at 14:51
  • 2
    My wild guess is that you are statically linking to a libusb shim that itself is dynamically linked to the DLL. You can probably run depwalker on the .lib you are linking to and see that it requires the DLL. Perhaps searching for libusb-specific information might help. – Shepmaster Jun 12 '19 at 14:52
  • 1
    *[If you are linking to the static LIB file you're done, it will build the code in to your executable. If you're linking to the LIB file for the DLL make sure the DLL is available in your execution path](https://stackoverflow.com/a/21050046/155423)*. So, check to see if there's a different `.lib` file that you can link against. It's also possible that vcpkg doesn't provide the fully static library. – Shepmaster Jun 12 '19 at 14:54
  • Dependency Walker says "No DOS or PE signature found. This file is not a valid [...] Windows module". – SlySherZ Jun 12 '19 at 15:13
  • 2
    Did you build libusb as a static library? vcpkg builds dynamic libraries by default. You must build with a [triplet](https://vcpkg.readthedocs.io/en/latest/users/triplets/) that requests static libraries. – Francis Gagné Jun 14 '19 at 04:37
  • Using a different triplet worked: `vcpkg.exe install libusb:x64-windows-static`. – SlySherZ Jun 14 '19 at 13:02

1 Answers1

3

@Shepmaster was right, the libusb:x64-windows triplet for vcpkg only contains definitions that try to call the real functions from the DLL.

I tried to load this library from a simple C program and I got the exact same error:

test.c

#include "C:\vcpkg\installed\x64-windows\include\libusb-1.0\libusb.h"

int main()
{
    const struct libusb_version *version = libusb_get_version();
    printf("Version %d.%d.%d", version->major, version->minor, version->micro);
}

compiled with:

cl test.c /link C:\vcpkg\installed\x64-windows\lib\libusb-1.0.lib

results in the same missing DLL error. But if I do with with the different libusb:x64-windows-static triplet:

test.c

#pragma comment(lib, "Advapi32.lib")
#include "C:\vcpkg\installed\x64-windows-static\include\libusb-1.0\libusb.h"

int main()
{
    const struct libusb_version *version = libusb_get_version();
    printf("Version %d.%d.%d", version->major, version->minor, version->micro);
}

compiled with:

cl test.c /link C:\vcpkg\installed\x64-windows-static\lib\libusb-1.0.lib

works just fine:

>test.exe
Version 1.0.22

To sum it up, if you want to statically link a Rust program against libusb, download vspkg and install vcpkg.exe install libusb:x64-windows-static. Set an environment variable LIBUSB_DIR that points to C:\vcpkg\installed\x64-windows-static and use the patched version of libusb-sys by putting this in your

Cargo.toml:

[dependencies]
libusb = "0.3"
libusb-sys = "0.2.3"

[patch.crates-io]
"libusb-sys" = { git = "https://github.com/cmsd2/libusb-sys" }
SlySherZ
  • 1,631
  • 1
  • 16
  • 25