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

2024-02-20

Edit:经过一番研究,我找到了部分解决方案。这link_name属性 https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute可用于更改链接到外部变量的名称。我现在的问题是这个属性是否可以自动应用bindgen解决链接错误。语境:

我正在尝试让我的 Rust 项目运行起来Julia https://julialang.org/的 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 to 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;
}

这会产生与之前相同的错误。然后我发现这个问题 https://stackoverflow.com/questions/66110929/unresolved-external-symbol-for-global-variable并意识到我需要像这样修改定义:

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

然后编译 C++ 程序,没有任何错误。然而,我无法找到 Rust 的等价物。

一些附加说明,我用来与 Julia 交互的实际代码使用bindgen生成 DLL 的 Rust 绑定。据我从链接器错误中可以看出,它们可以很好地处理 DLL 定义的函数,但在尝试访问全局变量时会失败。这与链接问题中的问题相符,其中添加 __declspec() 在函数上是可选的,但在变量上是必需的。

Edit:我发现使用这个名字__impl_jl_method_type没有__declspec(dllimport)注释会导致链接成功。这也适用于 Rust 代码。然而,由于我使用的实际代码是由bindgen生成的,所以缺少这个前缀。该代码还被一个板条箱使用,该板条箱提供了一个围绕绑定的安全包装器,因此似乎应该有一种方法可以让所有内容正确链接,而不必根据平台更改变量名称。


最终的解决方案是 Rust 的#[link(name="libname", kind="dylib")]注释函数如__declspec(dllimport)C++ 中的注释。我在构建脚本中添加了一些代码,以自动将此注释插入到 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;
}

以前的黑客解决方案以防未来的人发现它有用:

现在我已经找到了解决方法,将其添加到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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • 没有强命名的代码签名是否会让您的应用程序容易被滥用?

    尝试了解authenticode代码签名和强命名 我是否正确地认为 如果我对引用一些 dll 非强命名 的 exe 进行代码签名 恶意用户就可以替换我的 DLL 并以看似由我签名但正在运行的方式分发应用程序他们的代码 假设这是真的 那么您似
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • 重载<<的返回值

    include
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • SolrNet连接说明

    为什么 SolrNet 连接的容器保持静态 这是一个非常大的错误 因为当我们在应用程序中向应用程序发送异步请求时 SolrNet 会表现异常 在 SolrNet 中如何避免这个问题 class P static void M string
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • C# - OutOfMemoryException 在 JSON 文件上保存列表

    我正在尝试保存压力图的流数据 基本上我有一个压力矩阵定义为 double pressureMatrix new double e Data GetLength 0 e Data GetLength 1 基本上 我得到了其中之一pressur
  • 对来自流读取器的过滤数据执行小计

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐