如何将 Rust 编译为 LLVM 位码(包括依赖项)?

2023-12-09

我正在使用以下方法验证一些 Rust 代码SAW。 SAW 要求您编译为LLVM 位码,然后您可以导入并验证。我知道你可以使用以下命令生成位码--emit=llvm-bc标记为 rustc,这对于没有依赖项的项目非常有用。

当尝试编译使用外部板条箱的项目时,就会出现问题。下面是一个 Cargo.toml 文件示例:

[package]
name = "foobar"
version = "0.1.0"
edition = "2018"

[dependencies]
pythagoras = "0.1.1"

这是一个基本的src/lib.rs我们可能想要编译和验证:

pub use pythagoras;

#[no_mangle]
pub extern "C" fn calc_hypot(a: u32, b: u32) -> f64 {
    pythagoras::theorem(a, b)
}

我们可以将其编译为位码,如下所示:RUSTFLAGS="--emit=llvm-bc" cargo build --release。问题是当前模块及其依赖项的位码是单独生成的(在target/release/deps/foobar-something.bc and target/release/deps/pythagoras-somethingelse.bc)。它们仅在生成实际编译库时才会组合。

有没有办法生成包含当前模块及其所有依赖项的单个位码文件,以便可以导入该文件,并且不会引用任何外部名称?我意识到这是一个相当小众的案例,因此 hacky 解决方案(例如:编译为 C 静态库,然后以某种方式将其转换回 LLVM 位代码)也是完全合理的。


扩展于Aiden4评论:

  • 删除当前目标目录以防止使用任何旧工件:rm -r target/
  • 编译它RUSTFLAGS="--emit=llvm-bc" cargo build --release
  • 将位码文件链接在一起llvm-link target/release/deps/*.bc > withdeps.bc

那会让你almost所有依赖项。事实证明,所有 Rust 程序都隐式依赖于core or std虽然(虽然你可以用不稳定的方法来避免这种情况#![no_core],但祝你好运,实际上可以通过这种方式编译任何东西),所以你可能也想获得它的位码。

最简单的方法是将标准库从源代码编译为位代码。cargo为从源代码构建标准库提供实验支持,所以只需附加-Z build-std --target x86_64-unknown-linux-gnu(并根据需要更新目标)到您的cargo构建命令。使用时--target,这是由-Z build-std,构建文件放置在特定于目标的目录中,target/x86_64-unknown-linux-gnu/release/deps/在这种情况下。 targetless 目录包含标准库的构建依赖项:我们不希望这样!

我们不想链接all标准库的。我们真的只需要std及其依赖项:proc_macro这里不需要,因为我们正在编译为二进制文件,而不是过程宏。我们还需要链接到proc_abort or panic_unwind,将其与我们选择的展开代码生成设置相匹配。默认是展开的,所以我们删除另一个,proc_abort。让我们将这些库送去砧板:rm target/x86_64-unknown-linux-gnu/release/deps/{panic_abort,proc_macro}-*.bc.

这次我们尝试一下真正的链接:

rm -r target/
RUSTFLAGS="--emit=llvm-bc" cargo build --release -Z build-std --target x86_64-unknown-linux-gnu
rm target/x86_64-unknown-linux-gnu/release/deps/{panic_abort,proc_macro}-*.bc
llvm-link target/x86_64-unknown-linux-gnu/release/deps/*.bc > withalldeps.bc

是的,它成功了!好吧,除了对未定义函数的调用仍然设法溜过去。__rust_alloc, __rust_dealloc, __rust_realloc, and __rust_alloc_zeroed是使用 Rust 的 LLVM 分支时定义的神奇函数。标准库还依赖于libpthread and dlsym这些是与语言无关的库/函数,通常用 C 实现。您可以使用clang and a libc支持使用 Clang 进行编译的实现(GNU libc 不支持,我认为 musl 可能在这里工作?),以便在需要时得到它。此外,如果您正在编译为可执行文件,则很难找到main from _start.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将 Rust 编译为 LLVM 位码(包括依赖项)? 的相关文章

随机推荐