Rust:在 LLVM Bitcode 中包含依赖项

标签 rust compilation llvm llvm-ir

我正在使用 SAW 验证一些 Rust 代码. SAW 要求您编译为 LLVM bitcode ,然后您可以导入并验证。我知道您可以使用 --emit=llvm-bc 生成位码标记为 rustc,这对于没有依赖项的项目非常有用。
尝试编译使用外部 crate 的项目时会出现问题。这是 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 编译它
  • 将位码文件与 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构建命令。使用时--target ,这是 -Z build-std 所要求的,构建文件放在特定于目标的目录中,target/x86_64-unknown-linux-gnu/release/deps/在这种情况下。 targetless 目录包含标准库的构建依赖项:我们不想要那个!
    我们不想链接所有标准库。我们真的只需要std及其依赖项:proc_macro这里不需要,因为我们正在编译为二进制文件,而不是 proc 宏。我们还需要链接到 proc_abortpanic_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 , 和 __rust_alloc_zeroed是使用 Rust 的 LLVM 分支定义的魔法函数。标准库还依赖于 libpthread dlsym 它们是通常用 C 实现的语言无关库/函数。您可以使用 clang和一个 libc支持使用 Clang 编译的实现(GNU libc 不支持,我认为 musl 可能在这里工作?)以在需要时获得它。此外,如果您正在编译为可执行文件,则很难找到 main来自 _start .

    关于Rust:在 LLVM Bitcode 中包含依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69042049/

    相关文章:

    rust - Hyper 编译错误

    c++ - 我可以在构建项目时始终避免使用 cmake 或 make 吗?

    rust - 如何在Rust结构实现中编写SPI DMA代码?

    unit-testing - 为编译器阶段编写可维护的测试

    iphone - 何时使用 Xcode 分布式构建功能

    c++ -/lib64/libc.so.6标准解: version `GLIBC_2.14' not found

    Clang 集成汇编程序和 “unknown token in expression” 在否定期间

    xcode - C++ 虚拟表崩溃 LLVM/XCode

    llvm - 什么是有序和无序 LLVM CmpInst 比较指令?

    rust - 如何使用 serde 将结构序列化为另一个 Rust 数据结构?