在 cdylib Rust crate 中使用 ld 版本脚本
Using an ld version script in a cdylib Rust crate
我在构建 cdylib
Rust crate 时尝试使用版本脚本,但是由于 Rust 编译器创建的匿名版本脚本,我 运行 遇到了问题。我按照这个 forum post 了解如何添加版本脚本,但他们从未提到过这个问题。
执行
我正在使用 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 生成的版本脚本在创建后立即被删除,所以我实际上不知道它是什么样子。我试图跟随this answer查看其他版本的脚本,但是它被删除得太快了,我无法看到输出。
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;
};
itamarst在Rust forums上提供的解决方案。
说明
如问题所示,ld
不支持多版本脚本。但是,lld
这样做了,所以我们可以用它来代替。 (可以在 ubuntu 上使用 sudo apt install lld
安装)。要使用 lld
而不是 ld
,请将 -Clink-arg=-fuse-ld=lld
传递给 rustc
。
然而,这本身是不够的。 Rust 生成的版本脚本将优先,版本节点将不会按照我们的版本脚本中指定的那样应用。为了解决这个问题,可以给函数一个临时名称,并且可以通过链接器参数 (--defsym
) 将一个新符号链接到它。在版本脚本中可以自由使用新的符号,并且可以将原函数名标记为本地,以防止上传重复的符号。
Rust 代码
// 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;
};
建筑
在 cdylib
中,所有 rust/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
我在构建 cdylib
Rust crate 时尝试使用版本脚本,但是由于 Rust 编译器创建的匿名版本脚本,我 运行 遇到了问题。我按照这个 forum post 了解如何添加版本脚本,但他们从未提到过这个问题。
执行
我正在使用 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 生成的版本脚本在创建后立即被删除,所以我实际上不知道它是什么样子。我试图跟随this answer查看其他版本的脚本,但是它被删除得太快了,我无法看到输出。
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;
};
itamarst在Rust forums上提供的解决方案。
说明
如问题所示,ld
不支持多版本脚本。但是,lld
这样做了,所以我们可以用它来代替。 (可以在 ubuntu 上使用 sudo apt install lld
安装)。要使用 lld
而不是 ld
,请将 -Clink-arg=-fuse-ld=lld
传递给 rustc
。
然而,这本身是不够的。 Rust 生成的版本脚本将优先,版本节点将不会按照我们的版本脚本中指定的那样应用。为了解决这个问题,可以给函数一个临时名称,并且可以通过链接器参数 (--defsym
) 将一个新符号链接到它。在版本脚本中可以自由使用新的符号,并且可以将原函数名标记为本地,以防止上传重复的符号。
Rust 代码
// 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;
};
建筑
在 cdylib
中,所有 rust/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