Rust,如何使用DLL中的全局变量?C ++等效要求__declspec(dllimport) [英] Rust, how to use global variable from DLL? C++ equivalent requires __declspec(dllimport)

查看:175
本文介绍了Rust,如何使用DLL中的全局变量?C ++等效要求__declspec(dllimport)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过研究,我找到了部分解决方案. link_name 属性可用于更改链接到外部变量的名称.现在我的问题是 bindgen 是否可以自动应用此属性来解决链接错误.上下文:

After some research, I have found a partial solution. The link_name attribute can be used to change the name that is linked for an external variable. My question is now if this attribute can automatically be applied by bindgen to resolve linking errors. Context:

我正在尝试让我的Rust项目与 Julia 的C库一起使用.我将原始错误缩小为以下代码:

I am trying to get my Rust project to work with Julia's C library. I trimmed down my original error to this code:

// 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 ,以便链接程序可以找到它.)

(I also had to rename the file libjulia.dll.a to julia.lib so that the linker could find it.)

产生的错误如下:

note: reprod.jth9vslxkrhc6ez.rcgu.o : error LNK2019: 
    unresolved external symbol jl_method_type referenced 
    in function _ZN1reprod4main17hf5ae7fcf7e25b2b0E

要尝试进一步缩小,我在C ++中复制了它:

To try and trim it down further, I replicated it in 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;
}

这将产生与以前相同的错误.然后,我发现了此SO问题,并意识到我需要像这样修改定义:

This produces the same error as before. I then found this SO question and realized I needed to modify the definition like so:

extern "C" __declspec(dllimport) void* jl_method_type;

然后,C ++程序编译无任何错误.但是,我找不到与Rust相当的产品.

一些附加说明,我用来与Julia交互的实际代码使用 bindgen 为DLL生成Rust绑定.据我可以从链接器错误中看出,它们与DLL定义的功能配合正常,但在尝试访问全局变量时失败.这与链接问题中的问题一致,在链接问题中,在函数上添加__declspec()是可选的,但在变量上是必需的.

Some additional notes, the actual code I am using to interface with Julia uses bindgen to generate the Rust bindings for the DLL. As far as I can tell from the linker errors, they work fine with the functions defined by the DLL but fail when trying to access global variables. This lines up with the problem in the linked question where adding __declspec() was optional on functions but required on variables.

我发现使用名称 __ impl_jl_method_type 而不使用 __ declspec(dllimport)批注会导致成功链接.这也可以在Rust代码中使用.但是,由于我使用的实际代码是由bindgen生成的,因此缺少此前缀.板条箱还使用该代码,该板条箱为绑定提供了安全的包装,因此似乎应该有一种方法可以使所有内容正确链接,而不必根据平台更改变量名.

I have found that using the name __impl_jl_method_type without the __declspec(dllimport) annotation results in a successful link. This also works in the Rust code. However, since the actual code I am using is generated by bindgen, it is missing this prefix. The code is also used by a crate that provides a safe wrapper around the bindings, so it seems like there should be a way to get everything to link correctly without having to change the variable names depending on the platform.

推荐答案

最终的解决方案是Rust的#[link(name ="libname",kind ="dylib")] 注释功能,例如C ++中的 __ declspec(dllimport)注释.我在构建脚本中添加了一些代码,以自动将注释插入到bindgen为全局变量创建绑定的每个部分上方:

The ultimate solution turned out to be that Rust's #[link(name="libname", kind="dylib")] annotation functions like the __declspec(dllimport) annotation in C++. I added some code to my build script to automatically insert this annotation above every part where bindgen created bindings for a global variable:

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解决方案,以防将来有人发现它有用:

Previous hacky solution in case any future people find it useful:

目前,我已经找到了一种解决方法,方法是将其添加到生成绑定的 build.rs 文件中:

For now I have found a workaround by adding this to the build.rs file which generates the bindings:

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;
    }
}

它修改生成的代码,以使全局变量如下所示:

It modifies the generated code so that global variables look like this:

extern "C" {
    #[link_name = "__imp_jl_vararg_type"]
    pub static mut jl_vararg_type: *mut jl_unionall_t;
}

这篇关于Rust,如何使用DLL中的全局变量?C ++等效要求__declspec(dllimport)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆