考虑以下简单的程序:
int main()
{
printf("f[0] = %d\n", 0x0);
printf("f[0] = %d\n", 0x8);
return 0;
}
[root@comcat]$ gdb ./test
(gdb) disass main
Dump of assembler code for function main:
0x08048374 <main+0>: lea 0x4(%esp),%ecx
0x08048378 <main+4>: and $0xfffffff0,%esp
0x0804837b <main+7>: pushl -0x4(%ecx)
0x0804837e <main+10>: push %ebp
0x0804837f <main+11>: mov %esp,%ebp
0x08048381 <main+13>: push %ecx
0x08048382 <main+14>: sub $0x14,%esp
0x08048385 <main+17>: movl $0x0,0x4(%esp)
0x0804838d <main+25>: movl $0x8048480,(%esp)
0x08048394 <main+32>: call 0x80482d8 <printf@plt>
0x08048399 <main+37>: movl $0x8,0x4(%esp)
0x080483a1 <main+45>: movl $0x8048480,(%esp)
0x080483a8 <main+52>: call 0x80482d8 <printf@plt>
0x080483ad <main+57>: mov $0x0,%eax
0x080483b2 <main+62>: add $0x14,%esp
0x080483b5 <main+65>: pop %ecx
0x080483b6 <main+66>: pop %ebp
0x080483b7 <main+67>: lea -0x4(%ecx),%esp
0x080483ba <main+70>: ret
End of assembler dump.
(gdb) x /8i 0x80482d8
0x80482d8 <printf@plt>:
jmp *0x804958c ----> 0x804958c 处的值为 0x80482de,则其实际上跳到了下一条指令处。
0x80482de <printf@plt+6>: push $0x10
0x80482e3 <printf@plt+11>: jmp 0x80482a8 <_init+48>
0x80482e8: Cannot access memory at address 0x80482e8
(gdb) x /4x
0x804958c
0x804958c <_GLOBAL_OFFSET_TABLE_+20>:
0x080482de 0x00000000 0x00000000 0x0804949c
(gdb) b *0x0804838d
Breakpoint 1 at 0x804838d: file test.c, line 3.
(gdb) r
Starting program: /home/comcat/develop/test/dl/test
Breakpoint 1, 0x0804838d in main () at test.c:3
3 printf("f[0] = %d\n", 0);
(gdb) si
0x08048394 3 printf("f[0] = %d\n", 0);
(gdb)
0x080482d8 in printf@plt ()
(gdb)
0x080482de in printf@plt ()
(gdb)
0x080482e3 in printf@plt ()
(gdb) x /4i 0x80482a8
0x80482a8 <_init+48>: pushl 0x804957c
0x80482ae <_init+54>: jmp *
0x8049580
0x80482b4 <_init+60>: add %al,(%eax)
0x80482b6 <_init+62>: add %al,(%eax)
(gdb) x /4x
0x8049580
0x8049580 <_GLOBAL_OFFSET_TABLE_+8>:
0xb7fc02d0 0x080482be 0xb7e6e370 0x080482de
----> 0xb7fc02d0 处为动态链接器的解析函数 _dl_runtime_resolve()
(gdb) si
0x080482a8 in ?? ()
(gdb) si
0x080482ae in ?? ()
(gdb) si
0xb7fc02d0 in _dl_runtime_resolve () from /lib/ld-linux.so.2
(gdb) b *0x080483a1
Breakpoint 2 at 0x80483a1: file test.c, line 4.
(gdb) c
Continuing.
f[0] = 0
Breakpoint 2, 0x080483a1 in main () at test.c:4 ----> 第一次 printf 调用已经完,动态链接器已经修改 GOT 的相应项为 printf 的地址
4 printf("f[0] = %d\n", 8);
(gdb) x /8i 0x80482d8 ----> PLT 没有变化,但原 GOT 入口
0x804958c 处的值已经被改写为 printf 的地址
0x80482d8 <printf@plt>: jmp *
0x804958c
0x80482de <printf@plt+6>: push $0x10
0x80482e3 <printf@plt+11>: jmp 0x80482a8 <_init+48>
0x80482e8: add %al,(%eax)
0x80482ea: add %al,(%eax)
0x80482ec: add %al,(%eax)
0x80482ee: add %al,(%eax)
0x80482f0 <printf@plt+24>: xor %ebp,%ebp
(gdb) x /4x
0x804958c
0x804958c <_GLOBAL_OFFSET_TABLE_+20>:
0xb7e9f170 0x00000000 0x00000000 0x0804949c
(gdb) x /4i
0xb7e9f170
0xb7e9f170 <printf>: push %ebp
0xb7e9f171 <printf+1>: mov %esp,%ebp
0xb7e9f173 <printf+3>: push %ebx
0xb7e9f174 <printf+4>: call 0xb7e6e28f <__i686.get_pc_thunk.bx>
x86 下动态链接机制,动态链接器不会更改 PLT,只会在用到相应链接函数时,才会去解析符号,然后将对应的地址更新到 GOT 的相应项,GOT 里亦没有代码,不需要执行。
