首页 > 解决方案 > 在 cdylib Rust crate 中使用 ld 版本脚本

问题描述

我试图在构建cdylibRust 板条箱时使用版本脚本,但是由于 Rust 编译器创建的匿名版本脚本,我遇到了问题。我关注了这个关于如何添加版本脚本的论坛帖子,但他们从未提到过这个问题。

执行

cargo-make用来构建我的项目。在我的Makefile.toml我有这个任务:

[tasks.build]
toolchain = "nightly"  # Running with nightly-x86_64-unknown-linux-gnu
command = "cargo"
args = ["rustc", "--release", "-p", "my_crate", "--", "-C", "link-args=-Wl,--version-script=versions.map"]

运行cargo make build时,该任务执行此构建命令。

rustup run nightly cargo rustc --release -p my_crate -- -C link-args=-Wl,--version-script=versions.map

错误

但是,它不断产生此错误。据我所知,我的版本脚本(如下所示)与 Rust 生成的匿名版本脚本冲突(/tmp/rustcyXUHTy/list在错误中)。不幸的是,Rust 生成的版本脚本在创建后立即被删除,所以我实际上并不知道它是什么样子。我试图按照这个答案查看其他版本的脚本,但它被删除得太快了,我无法看到输出。

error: linking with `cc` failed: exit status: 1
  |
  = note: "cc" "-Wl,--version-script=/tmp/rustcyXUHTy/list" ... "-Wl,--version-script=versions.map"
  = note: /usr/bin/ld: anonymous version tag cannot be combined with other version tags
          collect2: error: ld returned 1 exit status

// I'm not completely sure which tags should be used and so far they have had no effect on the error
// #[no_mangle]
// #[export_name = "foo"]
pub unsafe extern "system" fn foo() {}

// The crate also contains other functions which are not covered by my version script
// I tried removing all of the other #[no_mangle] functions, but it had no effect
#[no_mangle]
pub unsafe extern "system" fn bar() {}

版本脚本

我对编写版本脚本不是很有经验,所以这是我想出的简单测试脚本。最终产品将使用来自现有 C 项目的类似版本脚本。

Project_1.0 {
    global:
        foo;
};

标签: rustlinkerld

解决方案


itamarst 在Rust 论坛上提供的解决方案。

解释

如题所示,ld不支持多版本脚本。但是,lld这样做我们可以改用它。(可以sudo apt install lld在 ubuntu 上安装)。要使用lld代替ld,请传递-Clink-arg=-fuse-ld=lldrustc

然而,这还不够。Rust 生成的版本脚本将优先,版本节点将不会按照我们的版本脚本中指定的方式应用。为了解决这个问题,可以给函数一个临时名称,并且可以通过链接器 args ( --defsym) 将一个新符号链接到它。在版本脚本中可以自由使用新符号,并且可以将原来的函数名标记为本地,以防止重复符号被上传。

锈代码

// Name function foo_impl and rename it on the command line
#[no_mangle]
pub unsafe extern "system" fn foo_impl() {}

版本脚本

Project_1.0 {
    global:
        foo;
    local:
        foo_inner;
};

建造

所有的cdylibrust/linker 参数都可以在build.rs.

// Tell Rust to use lld instead of ld
println!("cargo:rustc-cdylib-link-arg=-fuse-ld=lld");

// Set version script path
println!("cargo:rustc-cdylib-link-arg=-Wl,--version-script=mapfile");

// Rename symbols to get around the anonymous version script
for symbol in &["foo"] {
    println!("cargo:rustc-cdylib-link-arg=-Wl,--defsym={}={}_impl", symbol, symbol);
}

或者,所有这些参数都可以在命令行上传递。

cargo rustc -- -Clink-arg=-fuse-ld=lld -Clink-args=-Wl,--defsym=foo=foo_impl,--version-script=mapfile

推荐阅读