If we set a breakpoint in g and run our program, we can
see the current stack with the where command:
(gdb) where
#0 g () at example.c:4
#1 0x000055fc5f7d1184 in f () at example.c:9
#2 0x000055fc5f7d11b3 in main (argc=1, argv=0x7fff3464e5f8) at example.c:14
The frame at #0 is the current execution context, while
#1 is its caller, and so on. backtrace and
bt are synonyms for where.
If all we are trying to do is track down a segmentation fault, we
might not need to do anything other than run your program in GDB and use
where to find the offending statement.
Depending on the specific instruction causing an error, you might
need to look in the the calling frame. The up command lets
you change the active frame (the function being examined) by switching
to the current function’s caller. You can call this multiple times. The
reverse of this is down.
For example, if we run up at the previous breakpoint, we
see
(gdb) up
#1 0x000055fc5f7d1184 in f () at example.c:9
9 g();
This tells us that we’re now in frame #1, which is line
9 of example.c, in the function f, and we’ve
called g(). Running it again gives us
(gdb) up
#2 0x000055fc5f7d11b3 in main (argc=1, argv=0x7fff3464e5f8) at example.c:14
14 f();
which is the point in main where we called
f().
Running down twice takes us back to #0
(g):
(gdb) down
#1 0x000055fc5f7d1184 in f () at example.c:9
9 g();
(gdb) down
#0 g () at example.c:4
4 printf("in g()\n");
Every function call has an associated stack frame, which holds
function arguments, local variables, and any information we need to
address these or unwind to the calling function’s frame. We can see this
information with info frame. Both the info
command and the frame subcommand can be shortened to single
characters, so i frame, info f, and
i f all behave the same.
Here’s what we see in g:
(gdb) info frame
Stack level 0, frame at 0x7fff3464e4b0:
rip = 0x55fc5f7d1151 in g (example.c:4); saved rip = 0x55fc5f7d1184
called by frame at 0x7fff3464e4c0
source language c.
Arglist at 0x7fff3464e4a0, args:
Locals at 0x7fff3464e4a0, Previous frame's sp is 0x7fff3464e4b0
Saved registers:
rbp at 0x7fff3464e4a0, rip at 0x7fff3464e4a8
This tells us where the frame begins (0x7fff3464e4b0),
and all valid stack addresses for this frame will have lower
values than this (because the stack grows downward). It also
tells us the current value of rip, the instruction pointer
(pc on ARM platforms), which is the address of the next
instruction for the CPU to execute, as well as the saved
rip from the caller, which is the address of the next
instruction in the caller after this function returns. On AMD64 (the
most common 64-bit x86-family architecture), we store the base and
instruction pointers for the caller in our frame. For ARM, these are
typically only stored if this function is going to call another one.
info frame also takes an optional integer argument,
which equates to the frame level as shown by where. That
is, info frame 0 will show us information about the stack
frame of g, info frame 1 will show us
information about the stack frame of f, and
info frame 2 will show us information about the stack frame
of main.
where, backtrace, btinfo frame, i f