29

I am writing a Rust library containing an implementation of the callbacks for 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. The C++ examples provided by LLVM rely on the compiler intrinsic __builtin_return_address(0) in order to obtain this information.

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. The only reference I found is from an old version of Rust, but the function described is not available anymore. The function is the following:

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:

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. 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.

Concluding, is there a more straightforward way of getting the return address of the current function in Rust?

edit: The removal of the return_address intrinsic is discussed in this GitHub issue.

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. 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. In addition, the crate is using C libraries to extract the backtrace; this looks like something that should be done in pure Rust.

edit 3: The compiler intrinsic __builtin_return_address(0) generates a call to the LLVM intrinsic llvm.returnaddress. The corresponding documentation can be found here.

Elia Geretto
  • 387
  • 4
  • 10
  • It might be enlightening to extract the LLVM IR for the c++ intrinsic? (fire the c++ example through `clang -S -emit-llvm`) – l.k Apr 21 '19 at 23:59
  • `backtrace` in Rust (if I'm not mistaken) simply calls a C library (which happens to be a forked extract of `libgcc_eh` machinery) . Suppose, you can do the same. – oakad Jan 05 '20 at 11:14
  • Although not the return *address*, you may be interested in https://github.com/rust-lang/rust/issues/47809 – Jin-oh Kang Jan 23 '20 at 08:15
  • For this kind of project, I suppose a close instrumentation (that is, a "plugin" for rustc) is more suitable for this purpose. Debug symbols and/or compiler intermediates are still required for the return address to be useful, and it's not like you're going to change it or get the location of it (llvm.addressofreturnaddress) on the stack. – Jin-oh Kang Jan 23 '20 at 08:19

2 Answers2

2

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:

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Maurice Kayser
  • 455
  • 4
  • 11
  • This feature does not appear to be stable yet, but it's a perfect solution if one is using Rust nightly I guess. This is the issue the compiler is pointing me to when trying what you proposed: https://github.com/rust-lang/rust/issues/29602 – Elia Geretto Feb 20 '20 at 16:09
  • Can unstable features be removed, or just changed? – Sapphire_Brick Mar 21 '21 at 23:30
0

As of 2022 Maurice's answer doesn't work as-is and requires an additional argument.

#![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:

main address: 0x56261a13bb50
caller: 0x56261a13bbaf

Playground link;

Lander
  • 3,369
  • 2
  • 37
  • 53