Reputation: 394
I have recently been trying to use the __builtin_extract_return_addr function described here (https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) to obtain the encoded value of the RSP pointer. I am intentionally avoiding using __builtin_return_address(0) function, and solely trying to get the return address value to the caller using the RSP register.
The description of this function is as stated:
void * __builtin_extract_return_addr (void * addr)
The address as returned by __builtin_return_address may have to be fed through this function to get the actual encoded address. If no fixup is needed, this function simply passes through addr.
From my understanding, it seems as this function can take the arbitrary address and obtain the actual encoded address. (e.g., if
RSP 0x7fffffffe458 —▸ 0x40058e (main+14)
,
then using __builtin_extract_return_addr(0x7fffffffe458) should be 0x40058e
)
So I have this very simple test code I have been using to learn a bit about this, but not getting the values I am trying to get so I wanted to ask the question in the StackOverflow:
void print_sp() {
register void *sp asm ("rsp");
printf("%p\n", __builtin_extract_return_addr(sp));
void *addr = 0x7fffffffe458;
printf("%p\n", __builtin_extract_return_addr((addr)));
printf("%p\n", __builtin_return_address(0)); // I am trying to avoid using this
}
int main() {
print_sp();
}
In the first two lines of print_sp() function, I'm reading and printing the RSP register value, then using the builtin_extract_return_addr to see if I can get the encoded address of what is stored in the RSP register. This was a failure as I debugged using gdb, I am understanding it as the RSP register value at the time of this line is called will not have the return address to the caller.
In the second two lines of print_sp() function, I am hardcoding the void *addr to the value of 0x7fffffffe458, and then using this address value to see if I can get the decoded return address. The reason is that at the time of ret
instruction, the RSP value as shown
here is the following:
RSP 0x7fffffffe458 —▸ 0x40058e (main+14) ◂— mov eax, 0
To summarize, I am trying to get the return address value of 0x400578 without using the __builtin_return_address(0) value.
I have also tried to implement addq $8, %%rsp; jmpq -8(%%rsp)
using the inline assembly but to no avail. Something like this?
uintptr_t result;
asm volatile ( "mov %%rsp, %[value]\n\t"
"addq $8, %[value]\n\t"
: [value]"=a"(result)
: );
uintptr_t caller_address = (uintptr_t)__builtin_extract_return_addr(result);
Here is the disassembly of print_sp and main() function
Also, here are a few similar questions posted in the StackOverflow that I took a look: Retq instruction, where does it return Meaning of 0x8($rsp)
I hope the question makes sense. Please let me know if there is anything unclear and I will try to clarify them right away.
Thank you,
Upvotes: 0
Views: 2199
Reputation: 103
I am not sure why you are avoiding __builtin_return_address(0)
as it is probably the best option you can use to get the return address. If you can use it please do, but we will go along with that for now.
Your disassembly shows that you are using the frame pointer register in the function prologue, meaning that you are probably using zero optimization in compilation. In that case you can use rbp instead of rsp as it takes the value of rsp at the beginning of the function and remains constant. However, you will need to add +1 to the pointer you get from rbp (note that adding +1 to a long
pointer actually adds 8 bytes in x86_64).
This is done because before the mov instruction that copies rsp into rbp there is a push instruction that pushes the old rbp value to the stack, so our stack pointer stored in rsp have been decreased (the stack grows downward) by 8 bytes to hold the old frame pointer previously stored in rbp.
This process of setting up RBP as a frame pointer is optional in x86-64 calling conventions. 16-bit x86 used to require this, and some 32-bit calling conventions relied on it for backtraces, but x86-64 calling conventions don't. You can read about it if you need to know more about where the return address is stored in the stack relative to the frame pointer and stack pointer.
I think the use of register variables is deprecated so I will modify your second approach of inline assembly.
// works only if compiled without optimization, or -fno-omit-frame-pointer
// RBP points to the saved-RBP value just below the return address
void *bp;
asm ("movq %%rbp, %0\n"
: "=r" (bp));
void *ra;
ra = (void *) *((long *)bp + 1);
ra should now be your return address. Unfortunately, this only works if your compiler uses frame pointer register rbp, which does not usually hold if you use optimization while compiling. If you are using GCC for example any level other than -O0
will give you an error using this approach.
Just in case, you should use __builtin_extract_return_addr(ra)
. I didn't need it in my case. Also note that unless you are linking a non-PIE executable, the return address will be the value you see in the disassembly + executable start address (which is the location where your executable is loaded in memory). You can get this address by declaring extern char __executable_start;
globally then printing its address.
Upvotes: 2