Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust code cannot link with a C library compiled on Windows because there is an unresolved external symbol

I've been trying to get Rust to link with a C library on Windows, but Rust is unable to find the functions I need. It seems like my function signature is wrong, but Rust can find the .lib file.

I narrowed it down to very simple C and Rust code, but still can't figure out how to get it to work:

test.c -- compiled with Visual Studio 2015

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>

typedef struct MyStruct {
    int32_t i;
} MyStruct;

MyStruct my_func(const MyStruct *s) {
    MyStruct result;
    result.i = s->i + 1;
    return result;
}

int32_t my_func2() {
    return 42;
}

#ifdef __cplusplus
}
#endif

lib.rs

extern crate libc;

use libc::int32_t;

#[link(name = "MyLib")]
extern {
    pub fn my_func2() -> int32_t;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        unsafe {
            assert_eq!(42, my_func2());
        }
    }
}

Alternatively lib.rs:

extern crate libc;

use libc::int32_t;

#[repr(C)]
pub struct MyStruct {
    i: int32_t
}

#[link(name = "MyLib")]
extern {
    pub fn my_func(s: *mut MyStruct) -> MyStruct;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        unsafe {
            let mut s = MyStruct{ i: 10 };
            let s = my_func(&mut s as *mut MyStruct);
            assert_eq!(11, s.i);
        }
    }
}

This is the error message I get:

   Compiling test v0.1.0
error: linking with `link.exe` failed: exit code: 1120
  |
// snip (but the MyLib.lib is in here)
  = note: test-01aaa83d15a1dde3.0.o : error LNK2019: unresolved external symbol my_func referenced in function _ZN8test5tests8it_works17h52c29448f9d86a1aE
C:\<--omitted-->-01aaa83d15a1dde3.exe : fatal error LNK1120: 1 unresolved externals

error: aborting due to previous error

error: Could not compile `test`.

Cargo.toml

[package]
name = "test"
version = "0.1.0"
authors = ["<-- omitted -->"]

[dependencies]
libc = "0.2.17"
like image 238
Justin Avatar asked Nov 11 '16 12:11

Justin


1 Answers

Did you check to see that your .lib file has any symbols / exported functions?:

> dumpbin /exports /symbols MyLib.lib
Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file MyLib.lib

File Type: LIBRARY

This means that your .lib file does not have any exported symbols. You might have thought to use __declspec(dllexport), but that does not seem to work for static .lib files.

Visual Studio is a bit of a pain to work with, but here's how you get it working in VS 2015:

  1. Create a new "Module-Definition File" (.def)
  2. Fill in the details:

    LIBRARY MyLib
    EXPORTS
        my_func  @1
        my_func2 @2
    
  3. Right click on your project, click on properties, and make sure your project's configurations are correct:

    Configuration Type: Static library (.lib)

  4. Under Librarian, make sure you set the .def file:

    Module Definition File Name: test.def

  5. Build the .lib file. If you did this correctly, running dumpbin again should look something like this:

    > dumpbin /exports /symbols MyLib.lib
    Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file MyLib.lib
    
    File Type: LIBRARY
    
    COFF SYMBOL TABLE
    000 01015E97 ABS    notype       Static       | @comp.id
    001 00000000 SECT2  notype       External     | __IMPORT_DESCRIPTOR_MyLib
    002 C0000040 SECT2  notype       Section      | .idata$2
    003 00000000 SECT3  notype       Static       | .idata$6
    004 C0000040 UNDEF  notype       Section      | .idata$4
    005 C0000040 UNDEF  notype       Section      | .idata$5
    006 00000000 UNDEF  notype       External     | __NULL_IMPORT_DESCRIPTOR
    007 00000000 UNDEF  notype       External     | MyLib_NULL_THUNK_DATA
    
    String Table Size = 0x5C bytes
    
    COFF SYMBOL TABLE
    000 01015E97 ABS    notype       Static       | @comp.id
    001 00000000 SECT2  notype       External     | __NULL_IMPORT_DESCRIPTOR
    
    String Table Size = 0x1D bytes
    
    COFF SYMBOL TABLE
    000 01015E97 ABS    notype       Static       | @comp.id
    001 00000000 SECT2  notype       External     | MyLib_NULL_THUNK_DATA
    
    String Table Size = 0x22 bytes
    
         Exports
    
           ordinal    name
    
                 1    my_func
                 2    my_func2
    
      Summary
    
              D2 .debug$S
              14 .idata$2
              14 .idata$3
               8 .idata$4
               8 .idata$5
              12 .idata$6
    
  6. Copy the generated .lib file (ensuring you do so from the correct directory; note that 64 bit and 32 bit compile to different folders) to your Rust project's root directory:

    + MyRustProject/
     \
      + src/
      + Cargo.toml
      + MyLib.lib
    
  7. Go back to the build configurations and build a .dll:

    Configuration Type: Dynamic Library (.dll)

  8. Copy that .dll in the same place as the .lib

Now when you build your Rust project, it should work. Note that you must build your VS project for the same platform as the Rust one, so, in my case, that meant x64, rather than x86.

For more information on .def files, see Microsoft's official documentation.

like image 90
Justin Avatar answered Nov 14 '22 19:11

Justin