Rust:在LLVM位码中包含依赖项

人气:516 发布:2022-10-16 标签: compilation rust llvm llvm-ir

问题描述

我正在使用SAW验证一些Rust代码。SAW要求您编译为LLVM bitcode,然后可以导入和验证。我知道您可以使用--emit=llvm-bc标志来生成位码,这对于没有依赖项的项目非常有效。

尝试编译使用外部板条箱的项目时会出现此问题。下面是一个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。问题是,当前模块及其依赖项的Bitcode是单独生成的(在target/release/deps/foobar-something.bctarget/release/deps/pythagoras-somethingelse.bc中)。它们仅在生成实际编译的库时组合在一起。

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

谢谢!

推荐答案

展开Aiden4s评论:

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

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

最简单的方法是将标准库从源代码编译为位代码。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/。无目标目录包含标准库的构建依赖项:我们不希望出现这种情况!

我们不想链接所有标准库。我们实际上只需要std及其依赖项:这里不需要proc_macro,因为我们编译成二进制文件,而不是proc-宏。我们还需要链接proc_abortpanic_unwind,将其与我们选择的展开编码生成设置进行匹配。缺省值是Unding,所以我们删除另一个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__rust_alloc_zeroed是在使用Rust的LLVM fork时定义的魔术函数。标准库还依赖于libpthreaddlsym,它们是与语言无关的库/函数,通常用C实现。您可以使用clang和支持用Clang编译的libc实现(GNU libc不能,我认为MUSL可能在这里可以使用?)如果需要的话,去拿到那个。此外,如果您正在编译为可执行文件,则无法从_start中查找main

334