rust - Rust:在 LLVM Bitcode 中包含依赖项
问题描述
我正在使用SAW验证一些 Rust 代码。SAW 要求您编译为LLVM bitcode,然后您可以导入并验证它。我知道您可以使用 rustc 的--emit=llvm-bc
标志生成位码,这对于没有依赖关系的项目非常有用。
尝试编译使用外部 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.bc
和中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
这将为您提供几乎所有依赖项。事实证明,所有 Rust 程序都隐含依赖于其中之一core
或std
虽然(尽管您可以使用不稳定的方法来避免这种情况#![no_core]
,但祝您好运,实际上可以通过这种方式编译任何东西),因此您可能也想获得它的位码。
最简单的方法是将标准库从源代码编译为位码。cargo
对从 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-macro。我们还需要链接proc_abort
或链接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
, 和__rust_alloc_zeroed
是使用 Rust 的 LLVM fork 时定义的魔术函数。标准库还取决于哪些是通常在 C 中实现的libpthread
与语言无关的库/函数。您可以使用支持用 Clang 编译的实现(GNU libc 不支持,我认为 musl 可能在这里工作?)如果需要,得到它。此外,如果您正在编译为可执行文件,则很难从.dlsym
clang
libc
main
_start
推荐阅读
- javascript - 如何访问 NuxtJs 商店中的 localStorage?
- java - Liquibase 通过 Springboot 执行 postgres 方法
- sql - 在bigquery中根据id和时间对数据进行分组
- swift - Xcode 10.1 无法归档项目
- postgresql - 将整个 select 语句作为参数传递 - PostgreSQL 函数
- regex - 如何在linux中删除特定的“] [”
- c# - C#需要帮助实时修改datagridview
- maven - 在我的项目中添加了axis2作为maven依赖项
- java - 在 Eclipse 中输入 stream() 或 map() 而不使用箭头键
- python - 在 python ImportError 上导入 librosa 的问题:无法导入名称“iter_entry_points”