How function backtrace is generated

Let’s walk through a backtrace for a “Hello, world!” program, specifically simpler case with enabled frame pointers. Frame pointer is effectively an optional pointer, pointing to a portion of the stack dedicated to currently executing function. For ARM architecture, it occupies register r11.


int f1(int i) {
	return f2(i);
}

int main(void)
{
	return f1(3);
}

To enforce usage of frame pointers, -fno-omit-frame-pointer should be used:


gcc backtrace.c -fno-omit-frame-pointer -o backtrace

Omitting frame pointer is a performance optimization that makes r11 available for other uses.

Now to the backtrace itself: the idea is that with frame pointers enabled, there’s effectively a stack-based linked list of two-member structures, consisting of previous frame pointer and link register (pointer to caller).

Let’s stop at main, before calling f1:


0x1045c <<main+8>>                mov    r0, #3
0x10460 <<main+12>>               bl     0x1042c <<f1>>

(gdb) i r r11 lr
r11            0x7efff5dc
lr             0x76e8f678

Now let’s make a few more steps and stop exactly after prologue of f1:


0x1042c <<f1>>                    push   {r11, lr}
0x10430 <<f1+4>>                  add    r11, sp, #4
0x10434 <<f1+8>>                  sub    sp, sp, #8 

Two instructions in bold effectively add a node to the linked list. So that at any instruction inside f1, we could check the previous node link by looking at r11:


r11            0x7efff5d4

.. and examining a node that it points to:


(gdb) x/2x 0x7efff5d0
0x7efff5d0:     0x7efff5dc      0x00010464

(not sure why it points to the second member of the struct, that is where 4 bytes offset comes from, probably some ARM peculiarity)

Note that 0x7efff5dc is a previous value of FP during main, and 0x00010464 is an instruction inside main (can be seen by looking at main instruction addresses above). With this method, entire callstack can be recursively determined.

Determining backtrace without frame pointers is more complicated and generally less reliable. See this yosefk post for details.

Written on January 28, 2020