c++ - Rust,如何使用 DLL 中的全局变量? C++ 等效项需要 __declspec(dllimport)

标签 c++ rust linker dllimport

编辑:经过一番研究,我找到了部分解决方案。 link_name attribute可用于更改为外部变量链接的名称。我现在的问题是 bindgen 是否可以自动应用此属性解决链接错误。语境:
我正在尝试让我的 Rust 项目与 Julia 一起使用的 C 库。我将原始错误缩减为以下代码:

// main.rs
extern "C" {
    pub static mut jl_method_type: *mut ();
}

fn main() {
    println!("{:?}", unsafe { jl_method_type });
}

// build.rs
fn main() {
    println!("cargo:rustc-link-search=C:/path/to/julia/lib/");
    println!("cargo:rustc-link-lib=julia");
}
(我还必须将文件 libjulia.dll.a 重命名为 julia.lib 以便链接器可以找到它。)
产生的错误如下:
note: reprod.jth9vslxkrhc6ez.rcgu.o : error LNK2019: 
    unresolved external symbol jl_method_type referenced 
    in function _ZN1reprod4main17hf5ae7fcf7e25b2b0E
为了尝试进一步削减它,我在 C++ 中复制了它:
// Compile with clang -ljulia -LC:/path/to/julia/lib/ test.cpp -IC:/path/to/julia/include/julia/

extern "C" void* jl_method_type;

int main() {
    auto local = jl_method_type;
    return 0;
}
这会产生与以前相同的错误。然后我找到了this SO question并意识到我需要像这样修改定义:
extern "C" __declspec(dllimport) void* jl_method_type;
然后编译 C++ 程序没有任何错误。但是,我找不到 Rust 的等价物。
一些附加说明,我用来与 Julia 交互的实际代码使用 bindgen为 DLL 生成 Rust 绑定(bind)。据我从链接器错误中可以看出,它们可以正常使用 DLL 定义的函数,但在尝试访问全局变量时会失败。这与链接问题中的问题一致,其中添加 __declspec() 在函数上是可选的,但在变量上是必需的。
编辑:我发现使用名称 __impl_jl_method_type没有 __declspec(dllimport)注释导致成功的链接。这也适用于 Rust 代码。但是,由于我使用的实际代码是由 bindgen 生成的,因此缺少此前缀。该代码还被一个提供绑定(bind)绑定(bind)的安全包装器的 crate 使用,因此似乎应该有一种方法可以让所有内容正确链接,而无需根据平台更改变量名称。

最佳答案

最终的解决方案竟然是 Rust 的 #[link(name="libname", kind="dylib")]注释函数,如 __declspec(dllimport) C++ 中的注解。我在构建脚本中添加了一些代码,以便在 bindgen 为全局变量创建绑定(bind)的每个部分上方自动插入此注释:

let mut code = bindings.to_string();
if cfg!(target_os = "windows") {
    code = code.replace(
        "extern \"C\" {",
        "#[link(name = \"julia\", kind = \"dylib\")]\r\nextern \"C\" {",
    );
}

// Write the bindings to the $OUT_DIR/bindings.rs file.
let mut file = std::fs::File::create(&out_path).unwrap();
use std::io::Write;
file.write_all(code.as_bytes()).unwrap();
结果:
#[link(name="julia", kind="dylib")] 
extern "C" {
    pub static mut global_var: *mut some_bound_type_t;
}
以前的 hacky 解决方案,以防将来有人发现它有用:
现在我找到了一种解决方法,方法是将它添加到 build.rs生成绑定(bind)的文件:
let mut code = bindings.to_string();
if (cfg!(target_os = "windows")) {
    const BEFORE_GLOBAL: &'static str = "extern \"C\" {\r\n    pub static mut ";
    let mut prev_index = 0;
    while let Some(index) = code[prev_index..].find(BEFORE_GLOBAL) {
        let index = index + prev_index;
        let name_start = BEFORE_GLOBAL.len();
        let colon = code[index..].find(":").expect("Invalid syntax.");
        let name = &code[index..][name_start..colon];
        let annotation = format!("#[link_name = \"__imp_{}\"]\r\n    ", name);
        code.insert_str(index + "extern \"C\" {\r\n    ".len(), &annotation[..]);
        prev_index = index;
    }
}
它修改生成的代码,使全局变量如下所示:
extern "C" {
    #[link_name = "__imp_jl_vararg_type"]
    pub static mut jl_vararg_type: *mut jl_unionall_t;
}

关于c++ - Rust,如何使用 DLL 中的全局变量? C++ 等效项需要 __declspec(dllimport),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66181735/

相关文章:

rust - 如何让cargo-expand在宏内扩展宏

multithreading - 我可以安全地对不应该是多线程的东西进行多线程处理吗?

gcc - ld 找不到 -l<库>

c++ - 试图从 std::function 获取普通函数指针

c++ - 带有内存定位文件的 FFmpeg avformat_open_input

iphone - 如何在 C++ 中获取 iPhone 上的语言环境?

regex - 如何在 Rust 中将输入设置为原始字符串?

c - "TCPL"中的多源文件链接之谜

c++ - 为什么 C++ 不接受字符数组的有符号或无符号字符

c++ - 使用类似模板的机制调用具有相同签名的不同方法