在进入第二个断点之前,我们先来看看当前的寄存器信息,如下所示:
(gdb) i registers
rax 0x351658ff60 228008197984
rbx 0x0 0
rcx 0x0 0
rdx 0x7fffffffe4e8 140737488348392
rsi 0x7fffffffe4d8 140737488348376
rdi 0x1 1
rbp 0x7fffffffe3f0 0x7fffffffe3f0
rsp 0x7fffffffe3e0 0x7fffffffe3e0
r8 0x351658e300 228008190720
r9 0x3515a0e9d0 227996133840
r10 0x7fffffffe240 140737488347712
r11 0x351621ebe0 228004588512
r12 0x400390 4195216
r13 0x7fffffffe4d0 140737488348368
r14 0x0 0
r15 0x0 0
rip 0x4004d4 0x4004d4 <main+8>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
我们看到,在第一个断点处,也就是main函数中调用func()的位置,此时调用函数func()所需要的参数还没有存储到寄存器中,此时是在sub $0x10,%rsp汇编指令之后的位置。
接下来我们进入第二个断点,即设置在func()函数的断点,查看此时的寄存器信息,如下所示:
(gdb) i registers
rax 0x351658ff60 228008197984
rbx 0x0 0
rcx 0x4 4
rdx 0x3 3
rsi 0x400608 4195848
rdi 0xc 12
rbp 0x7fffffffe3d0 0x7fffffffe3d0
rsp 0x7fffffffe3a0 0x7fffffffe3a0
r8 0x5 5
r9 0x6 6
r10 0x7fffffffe240 140737488347712
r11 0x351621ebe0 228004588512
r12 0x400390 4195216
r13 0x7fffffffe4d0 140737488348368
r14 0x0 0
r15 0x0 0
rip 0x4004ac 0x4004ac <func+29>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
此时在rsi、rdi等寄存器中已经可以看到我们传递的参数值,我们此时的位置是在callq 0x40048f <func>之后的位置。
反汇编func()函数,如下所示:
(gdb) disassemble /m func
Dump of assembler code for function func:
14 {
0x000000000040048f <+0>: push%rbp
0x0000000000400490 <+1>: mov%rsp,%rbp
0x0000000000400493 <+4>: sub $0x30,%rsp
0x0000000000400497 <+8>: mov%edi,-0x14(%rbp)
0x000000000040049a <+11>: mov%rsi,-0x20(%rbp)
0x000000000040049e <+15>: mov%edx,-0x24(%rbp)
0x00000000004004a1 <+18>: mov%ecx,-0x28(%rbp)
0x00000000004004a4 <+21>: mov%r8d,-0x2c(%rbp)
0x00000000004004a8 <+25>: mov%r9d,-0x30(%rbp)
15 int ret;
16
17 ret = arg7+ arg8;
=> 0x00000000004004ac<+29>: mov 0x18(%rbp),%eax
0x00000000004004af <+32>: mov 0x10(%rbp),%edx
0x00000000004004b2 <+35>: lea (%rdx,%rax,1),%eax
0x00000000004004b5 <+38>: mov%eax,-0x4(%rbp)
18
19 func2(fd, arg3);
0x00000000004004b8 <+41>: mov-0x24(%rbp),%edx
0x00000000004004bb <+44>: mov-0x14(%rbp),%eax
0x00000000004004be <+47>: mov%edx,%esi
0x00000000004004c0 <+49>: mov%eax,%edi
0x00000000004004c2 <+51>: callq 0x400474<func2>
20
21 return ret;
0x00000000004004c7 <+56>: mov-0x4(%rbp),%eax
22 }
0x00000000004004ca <+59>: leaveq
0x00000000004004cb <+60>: retq
从上面的汇编代码可以看到,在做具体的操作之前,会将寄存器中的参数值压入到栈上,并且在此后的操作中,都是只会去操作栈上的值,而不是直接修改寄存器中的值。
现在有一个问题,我们在第二个断点处,是在将寄存器压入栈之前还是之后?如果此时打印fd,是从栈上取值还是寄存器中取值?这个问题也很好判断,直接使用p命令打印fd即可,具体过程如下所示:
(gdb) p fd
$9 = 12
(gdb) p $rdi
$10 = 12
(gdb) set $rdi=15
(gdb) p fd
$11 = 12
(gdb) set *(int *)($rbp-0x14)=15
(gdb) p fd
$12 = 15
(gdb)