简体   繁体   English

如何获取 function 的返回地址?

[英]How do I get the return address of a function?

I am writing a Rust library containing an implementation of the callbacks for LLVM SanitizerCoverage .我正在编写一个 Rust 库,其中包含 LLVM SanitizerCoverage回调的实现。 These callbacks can be used to trace the execution of an instrumented program.这些回调可用于跟踪检测程序的执行。

A common way to produce a trace is to print the address of each executed basic block.产生跟踪的一种常见方法是打印每个已执行的基本块的地址。 However, in order to do that, it is necessary to retrieve the address of the call instruction that invoked the callback.然而,为了做到这一点,有必要检索调用回调的call指令的地址。 The C++ examples provided by LLVM rely on the compiler intrinsic __builtin_return_address(0) in order to obtain this information. 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);
}

I am trying to reproduce the same function in Rust but, apparently, there is no equivalent to __builtin_return_address .我试图在 Rust 中重现相同的 function 但显然,没有等同于__builtin_return_address The only reference I found is from an old version of Rust, but the function described is not available anymore.我找到的唯一参考来自旧版本Rust,但描述的 function 已不可用。 The function is the following: function 如下:

pub unsafe extern "rust-intrinsic" fn return_address() -> *const u8

My current hacky solution involves having a C file in my crate that contains the following function:我当前的 hacky 解决方案涉及在我的板条箱中包含一个 C 文件,其中包含以下 function:

void* get_return_address() {
  return __builtin_return_address(1);
}

If I call it from a Rust function, I am able to obtain the return address of the Rust function itself.如果我从 Rust function 调用它,我可以获得 Rust function 本身的返回地址。 This solution, however, requires the compilation of my Rust code with -C force-frame-pointers=yes for it to work, since the C compiler intrinsic relies on the presence of frame pointers.但是,此解决方案需要使用-C force-frame-pointers=yes编译我的 Rust 代码才能工作,因为 C 编译器固有地依赖于帧指针的存在。

Concluding, is there a more straightforward way of getting the return address of the current function in Rust?总结一下,在Rust中获取当前function的返回地址有没有更直接的方法?

edit: The removal of the return_address intrinsic is discussed in this GitHub issue.编辑:这个GitHub 问题中讨论了return_address内在函数的删除。

edit 2: Further testing showed that the backtrace crate is able to correctly extract the return address of the current function, thus avoiding the hack I described before.编辑 2:进一步的测试表明backtrace crate 能够正确提取当前 function 的返回地址,从而避免了我之前描述的 hack。 Credit goes to this tweet.归功于这条推文。

The problem with this solution is the overhead that is generated creating a full backtrace when only the return address of the current function is needed.此解决方案的问题是当仅需要当前 function 的返回地址时创建完整回溯所产生的开销。 In addition, the crate is using C libraries to extract the backtrace;此外,该箱子使用 C 库来提取回溯; this looks like something that should be done in pure Rust.这看起来像是应该在纯 Rust 中完成的事情。

edit 3: The compiler intrinsic __builtin_return_address(0) generates a call to the LLVM intrinsic llvm.returnaddress .编辑 3:编译器内部__builtin_return_address(0)生成对 LLVM 内部llvm.returnaddress的调用。 The corresponding documentation can be foundhere .可以在此处找到相应的文档。

I could not find any official documentation about this, but found out by asking in the rust-lang repository : You can link against LLVM intrinsics, like llvm.returnaddress , with only a few lines of code:我找不到任何关于此的官方文档,但通过rust-lang存储库中询问发现:您可以链接到 LLVM 内在函数,例如llvm.returnaddress ,只需几行代码:

extern {
    #[link_name = "llvm.returnaddress"]
    fn return_address() -> *const u8;
}

fn foo() {
    println!("I was called by {:X}", return_address());
}

The LLVM intrinsic llvm.addressofreturnaddress might also be interesting. LLVM 固有的llvm.addressofreturnaddress也可能很有趣。

Probably not, because any approach that you took would depend on frame pointers to locate the return address. 可能不是,因为你采取的任何方法都取决于帧指针来定位返回地址。 I have done similar things in C, and the result relied as well on frame pointers, not to mention inline assembly. 我在C中做过类似的事情,结果也依赖于帧指针,更不用说内联汇编了。 Count your blessings that you don't need that! 算上你不需要的祝福吧!

As of 2022 Maurice's answer doesn't work as-is and requires an additional argument.截至 2022 年,莫里斯的回答不再按原样工作,需要额外论证。

#![feature(link_llvm_intrinsics)]

extern {
    #[link_name = "llvm.returnaddress"]
    fn return_address(a: i32) -> *const u8;
}

macro_rules! caller_address {
    () => {
        unsafe { return_address(0) }
    };
}


fn print_caller() {
    println!("caller: {:p}", caller_address!());
}

fn main() {
    println!("main address: {:p}", main as *const ());
    print_caller();
}

Output: Output:

main address: 0x56261a13bb50
caller: 0x56261a13bbaf

Playground link ; 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM