Rust,如何使用 DLL 中的全局变量? C++ 等效项需要 __declspec(dllimport)
Rust, how to use global variable from DLL? C++ equivalent requires __declspec(dllimport)
编辑: 经过一番研究,我找到了部分解决方案。 link_name
attribute 可用于更改外部变量的 linked 名称。我现在的问题是 bindgen
是否可以自动应用此属性来解决 linking 错误。上下文:
我正在尝试让我的 Rust 项目与 Julia 的 C 库一起工作。我 trim将我最初的错误改写为这段代码:
// 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
以便 link 用户可以找到它。)
产生的错误如下:
note: reprod.jth9vslxkrhc6ez.rcgu.o : error LNK2019:
unresolved external symbol jl_method_type referenced
in function _ZN1reprod4main17hf5ae7fcf7e25b2b0E
为了进一步尝试 trim 它,我用 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 绑定。据我所知 linker 错误,它们可以很好地处理 DLL 定义的函数,但在尝试访问全局变量时失败。这与 linked 问题中的问题一致,其中添加 __declspec() 在函数上是可选的,但在变量上是必需的。
编辑: 我发现使用不带 __declspec(dllimport)
注释的名称 __impl_jl_method_type
会导致成功 link。这也适用于 Rust 代码。但是,由于我使用的实际代码是由 bindgen 生成的,因此缺少此前缀。该代码也被一个提供绑定安全包装的板条箱使用,因此似乎应该有一种方法可以将所有内容正确地设置为 link,而不必根据平台更改变量名称。
最终的解决方案是 Rust 的 #[link(name="libname", kind="dylib")]
注解功能类似于 C++ 中的 __declspec(dllimport)
注解。我在构建脚本中添加了一些代码,以在 bindgen 为全局变量创建绑定的每个部分上方自动插入此注释:
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
文件中:
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;
}
编辑: 经过一番研究,我找到了部分解决方案。 link_name
attribute 可用于更改外部变量的 linked 名称。我现在的问题是 bindgen
是否可以自动应用此属性来解决 linking 错误。上下文:
我正在尝试让我的 Rust 项目与 Julia 的 C 库一起工作。我 trim将我最初的错误改写为这段代码:
// 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
以便 link 用户可以找到它。)
产生的错误如下:
note: reprod.jth9vslxkrhc6ez.rcgu.o : error LNK2019:
unresolved external symbol jl_method_type referenced
in function _ZN1reprod4main17hf5ae7fcf7e25b2b0E
为了进一步尝试 trim 它,我用 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 绑定。据我所知 linker 错误,它们可以很好地处理 DLL 定义的函数,但在尝试访问全局变量时失败。这与 linked 问题中的问题一致,其中添加 __declspec() 在函数上是可选的,但在变量上是必需的。
编辑: 我发现使用不带 __declspec(dllimport)
注释的名称 __impl_jl_method_type
会导致成功 link。这也适用于 Rust 代码。但是,由于我使用的实际代码是由 bindgen 生成的,因此缺少此前缀。该代码也被一个提供绑定安全包装的板条箱使用,因此似乎应该有一种方法可以将所有内容正确地设置为 link,而不必根据平台更改变量名称。
最终的解决方案是 Rust 的 #[link(name="libname", kind="dylib")]
注解功能类似于 C++ 中的 __declspec(dllimport)
注解。我在构建脚本中添加了一些代码,以在 bindgen 为全局变量创建绑定的每个部分上方自动插入此注释:
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
文件中:
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;
}