Rust:在 LLVM Bitcode 中包含依赖项

Rust: Including dependenies in LLVM Bitcode

我正在使用 SAW. SAW requires that you compile to LLVM bitcode 验证一些 Rust 代码,然后您可以导入并验证这些代码。我知道您可以使用 --emit=llvm-bc 标志生成 bitcode 到 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.bctarget/release/deps/pythagoras-somethingelse.bc 中)。它们仅在生成实际编译库时才合并。

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

谢谢!

扩展 Aiden4 的评论:

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

这将使您 几乎 所有依赖项。事实证明,所有 Rust 程序都隐式依赖于 corestd(虽然你可以使用不稳定的 #![no_core] 来避免这种情况,但祝你好运,实际上可以通过这种方式编译任何东西) ,因此您可能也想获取它的位码。

最简单的方法是将标准库从源代码编译为位代码。 cargo has experimental support for building the standard libraries from source,因此只需将 -Z build-std --target x86_64-unknown-linux-gnu(并根据需要更新目标)附加到您的 cargo 构建命令。当使用 -Z build-std 所要求的 --target 时,构建文件将放在特定于目标的目录中,在本例中为 target/x86_64-unknown-linux-gnu/release/deps/。无目标目录包含标准库的构建依赖项:我们不想要那个!

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

让我们这次 link 真正地尝试一下:

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__rust_alloc_zeroed 是使用 Rust 的 LLVM 分支时定义的魔术函数。标准库还依赖于 libpthreaddlsym,它们是通常在 C 中实现的与语言无关的 libraries/functions。您可以使用 clanglibc 实现支持使用 Clang 编译(GNU libc 不支持,我认为 musl 可能在这里工作?)以便在需要时获得它。此外,如果您正在编译为可执行文件,则无法从 _start.

中找到 main