我正在编写一个Rust库,其中包含LLVM SanitizerCoverage的回调的实现。这些回调可用于跟踪已检测程序的执行。
产生跟踪的一种常见方法是打印每个执行的基本块的地址。但是,为此,必须检索调用回调的call
指令的地址。 LLVM提供的C ++示例依赖于编译器固有的__builtin_return_address(0)
来获取此信息。
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return;
void *PC = __builtin_return_address(0);
printf("guard: %p %x PC %p\n", guard, *guard, PC);
}
我正在尝试在Rust中重现相同的功能,但是显然没有与__builtin_return_address
等效的功能。我发现的唯一参考来自Rust的old version,但是所描述的功能不再可用。该函数如下:
pub unsafe extern "rust-intrinsic" fn return_address() -> *const u8
我当前的骇客解决方案涉及在我的箱子中包含一个C文件,该文件包含以下功能:
void* get_return_address() {
return __builtin_return_address(1);
}
如果我从Rust函数调用它,我就能获得Rust函数本身的返回地址。但是,此解决方案要求使用-C force-frame-pointers=yes
编译我的Rust代码才能使其正常工作,因为C编译器固有的功能依赖于帧指针的存在。
最后,有没有更简单的方法来获取Rust中当前函数的返回地址?
编辑:在this GitHub问题中讨论了如何删除return_address
内部函数。
编辑2:进一步的测试表明,backtrace
板条箱能够正确提取当前函数的返回地址,从而避免了我之前描述的hack。感谢this的推文。
此解决方案的问题是仅在需要当前函数的返回地址时生成完整回溯的开销。另外,板条箱使用C库提取回溯。看起来应该在纯Rust中完成。
编辑3:。编译器内部__builtin_return_address(0)
会生成对LLVM内部llvm.returnaddress
的调用。可以在here中找到相应的文档。
答案 0 :(得分:2)
我找不到与此有关的任何官方文档,但通过asking in the rust-lang
repository找到了:您可以使用几行代码来链接LLVM内部函数,例如llvm.returnaddress
:
extern {
#[link_name = "llvm.returnaddress"]
fn return_address() -> *const u8;
}
fn foo() {
println!("I was called by {:X}", return_address());
}
LLVM内部llvm.addressofreturnaddress
也可能很有趣。
答案 1 :(得分:0)
可能不是,因为您采用的任何方法都将依赖于帧指针来定位返回地址。我在C中做过类似的事情,结果也依赖于框架指针,更不用说内联汇编了。算上您不需要的祝福!