Rust 代码不能 link 编译在 Windows 上的 C 库,因为有一个未解析的外部符号

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

我一直在尝试通过 Windows 上的 C 库使 Rust 达到 link,但 Rust 无法找到我需要的函数。看起来我的函数签名是错误的,但是 Rust 可以找到 .lib 文件。

我将它缩小到非常简单的 C 和 Rust 代码,但仍然无法弄清楚如何让它工作:

test.c -- 用 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());
        }
    }
}

或者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);
        }
    }
}

这是我收到的错误消息:

   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"

您是否检查过您的 .lib 文件是否有任何符号/导出函数?:

> 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

这意味着您的 .lib 文件没有任何导出的符号。您可能想过使用 __declspec(dllexport),但这似乎不适用于静态 .lib 文件。

Visual Studio 使用起来有点麻烦,但这是在 VS 2015 中使用它的方法:

  1. 新建 "Module-Definition File" (.def)
  2. 填写详细信息:

    LIBRARY MyLib
    EXPORTS
        my_func  @1
        my_func2 @2
    
  3. 右键单击您的项目,单击属性,并确保您的项目配置正确:

  4. 在 Librarian 下,确保设置 .def 文件:

  5. 构建 .lib 文件。如果你这样做正确,运行 dumpbin 应该看起来像这样:

    > 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
    003 00000000 SECT3  notype       Static       | .idata
    004 C0000040 UNDEF  notype       Section      | .idata
    005 C0000040 UNDEF  notype       Section      | .idata
    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
              14 .idata
               8 .idata
               8 .idata
              12 .idata
    
  6. 复制生成的 .lib 文件(确保你从正确的目录这样做;注意 64 位和 32 位编译到不同的文件夹)到你的 Rust 项目的根目录:

    + MyRustProject/
     \
      + src/
      + Cargo.toml
      + MyLib.lib
    
  7. 返回构建配置并构建一个 .dll:

  8. .dll 复制到与 .lib

  9. 相同的位置

现在,当您构建 Rust 项目时,它应该可以工作了。请注意,您必须为与 Rust 相同的平台构建 VS 项目,因此,在我的例子中,这意味着 x64,而不是 x86。

有关 .def 个文件的详细信息,请参阅 Microsoft's official documentation.