让我们以这个小示例代码为例:
#include <stdio.h>
#include <stdlib.h>
int foo (int a)
{
return a ? a << 2: 1000;
}
int main()
{
printf("The result of foo(10) is %d\n", foo(10));
return EXIT_SUCCESS;
}
一旦组装,我们得到:
0000000000400506 <foo>:
400506: 55 push %rbp
400507: 48 89 e5 mov %rsp,%rbp
40050a: 89 7d fc mov %edi,-0x4(%rbp)
40050d: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
400511: 74 08 je 40051b <foo+0x15>
400513: 8b 45 fc mov -0x4(%rbp),%eax
400516: c1 e0 02 shl $0x2,%eax
400519: eb 05 jmp 400520 <foo+0x1a>
40051b: b8 e8 03 00 00 mov $0x3e8,%eax
400520: 5d pop %rbp
400521: c3 retq
0000000000400522 <main>:
400522: 55 push %rbp
400523: 48 89 e5 mov %rsp,%rbp
400526: bf 0a 00 00 00 mov $0xa,%edi
40052b: e8 d6 ff ff ff callq 400506 <foo>
400530: 89 c6 mov %eax,%esi
400532: bf d4 05 40 00 mov $0x4005d4,%edi
400537: b8 00 00 00 00 mov $0x0,%eax
40053c: e8 9f fe ff ff callq 4003e0 <printf@plt>
400541: b8 00 00 00 00 mov $0x0,%eax
400546: 5d pop %rbp
400547: c3 retq
400548: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40054f: 00
让我们看看下面的gdb会话:
(gdb) break *0x40052b
Breakpoint 1 at 0x40052b: file frame.c, line 13.
(gdb) break *0x400530
Breakpoint 2 at 0x400530: file frame.c, line 13.
我们只是在foo过程调用之前和之后设置了一个断点。
(gdb) break foo
Breakpoint 3 at 0x40050d: file frame.c, line 7.
我们在foo程序中设置了一个断点。
(gdb) run
Starting program: /home/fleury/tmp/tests/frame
Breakpoint 1, 0x000000000040052b in main () at frame.c:13
13 printf("The result of foo(10) is %d\n", foo(10));
我们启动程序并在调用foo.
(gdb) info frame
Stack level 0, frame at 0x7fffffffe0c0:
rip = 0x40052b in main (frame.c:13); saved rip = 0x7ffff7a54b45
source language c.
Arglist at 0x7fffffffe0b0, args:
Locals at 0x7fffffffe0b0, Previous frame's sp is 0x7fffffffe0c0
Saved registers:
rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8
我们询问了有关堆栈帧环境的信息。我们可以注意到save rip = 0x7ffff7a54b45(这是程序的返回地址main)。
(gdb) continue
Continuing.
Breakpoint 3, foo (a=10) at frame.c:7
7 return a ? a << 2: 1000;
我们继续执行程序并在foo程序内部停止(第三个断点)。让我们询问堆栈帧:
(gdb) info frame
Stack level 0, frame at 0x7fffffffe0b0:
rip = 0x40050d in foo (frame.c:7); saved rip = 0x400530
called by frame at 0x7fffffffe0c0
source language c.
Arglist at 0x7fffffffe0a0, args: a=10
Locals at 0x7fffffffe0a0, Previous frame's sp is 0x7fffffffe0b0
Saved registers:
rbp at 0x7fffffffe0a0, rip at 0x7fffffffe0a8
请注意,saved rip = 0x400530这正是call foo.
(gdb) continue
Continuing.
Breakpoint 2, 0x0000000000400530 in main () at frame.c:13
13 printf("The result of foo(10) is %d\n", foo(10));
我们继续执行,并在foo程序出口处到达第二个断点。再次,让我们询问退货地址:
(gdb) info frame
Stack level 0, frame at 0x7fffffffe0c0:
rip = 0x400530 in main (frame.c:13); saved rip = 0x7ffff7a54b45
source language c.
Arglist at 0x7fffffffe0b0, args:
Locals at 0x7fffffffe0b0, Previous frame's sp is 0x7fffffffe0c0
Saved registers:
rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8
当我们弹出时它已经恢复到原始值foo。
事实上,info frame(缩写为i f)还告诉在哪里可以找到堆栈上存储的返回地址:
Saved registers:
rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8
而且,如果您要求gdb显示内容,0x7fffffffe0b8您应该看到0x7ffff7a54b45:
(gdb) print /x *0x7fffffffe0b8
$1 = 0x7ffff7a54b45