考虑以下简单的程序:
int main()
{
printf(
"f[0] = %d\n", 0x0);
printf(
"f[0] = %d\n", 0x8);
return 0;
}
root@comcat:/root> gdb ./test
(gdb)
disass main
Dump of assembler code for function main:
0x1000044c <main+0>: stwu r1,-16(r1)
0x10000450 <main+4>: mflr r0
0x10000454 <main+8>: stw r0,20(r1)
0x10000458 <main+12>: stw r31,12(r1)
0x1000045c <main+16>: mr r31,r1
0x10000460 <main+20>: lis r9,4096
0x10000464 <main+24>: addi r3,r9,2056
0x10000468 <main+28>: li r4,0
0x1000046c <main+32>: crclr 4*cr1+eq
0x10000470 <main+36>: bl
0x100109c0 <printf@plt>
0x10000474 <main+40>: lis r9,4096
0x10000478 <main+44>: addi r3,r9,2056
0x1000047c <main+48>: li r4,8
0x10000480 <main+52>: crclr 4*cr1+eq
0x10000484 <main+56>: bl
0x100109c0 <printf@plt>
0x10000488 <main+60>: li r0,0
0x1000048c <main+64>: mr r3,r0
0x10000490 <main+68>: lwz r11,0(r1)
0x10000494 <main+72>: lwz r0,4(r11)
0x10000498 <main+76>: mtlr r0
0x1000049c <main+80>: lwz r31,-4(r11)
0x100004a0 <main+84>: mr r1,r11
0x100004a4 <main+88>: blr
End of assembler dump.
(gdb)
b *0x10000468
Breakpoint 1 at 0x10000468: file enum.c, line 13.
(gdb)
r
Starting program: /root/test
Breakpoint 1, 0x10000468 in main () at test.c:13
13 test.c: No such file or directory.
in test.c
(gdb)
x /6i 0x100109c0
0x100109c0 <printf@plt>:
li r11,8
0x100109c4 <printf@plt+4>: b 0x10010988 <_GLOBAL_OFFSET_TABLE_+44>
0x100109c8 <printf@plt+8>: .long 0x0
0x100109cc <printf@plt+12>: .long 0x0
0x100109d0 <printf@plt+16>: .long 0x0
0x100109d4 <completed.6562>: .long 0x0
(gdb)
x /10i 0x10010988
0x10010988 <_GLOBAL_OFFSET_TABLE_+44>: rlwinm r12,r11,1,0,30
0x1001098c <_GLOBAL_OFFSET_TABLE_+48>: add r11,r12,r11
0x10010990 <_GLOBAL_OFFSET_TABLE_+52>: li r12,14468
0x10010994 <_GLOBAL_OFFSET_TABLE_+56>: addis r12,r12,4093
0x10010998 <_GLOBAL_OFFSET_TABLE_+60>: mtctr r12
0x1001099c <_GLOBAL_OFFSET_TABLE_+64>: li r12,0
0x100109a0 <_GLOBAL_OFFSET_TABLE_+68>: addis r12,r12,12288
0x100109a4 <_GLOBAL_OFFSET_TABLE_+72>: bctr
0x100109a8 <_GLOBAL_OFFSET_TABLE_+76>: .long 0x0
0x100109ac <_GLOBAL_OFFSET_TABLE_+80>: .long 0x0
(gdb) si
0x1000046c 13 in enum.c
(gdb) si
0x10000470 13 in enum.c
(gdb)
0x100109c0 in printf@plt ()
(gdb)
0x100109c4 in printf@plt ()
(gdb)
0x10010988 in ?? ()
(gdb)
0x1001098c in ?? ()
(gdb)
0x10010990 in ?? ()
(gdb)
0x10010994 in ?? ()
(gdb)
0x10010998 in ?? ()
(gdb)
0x1001099c in ?? ()
(gdb)
0x100109a0 in ?? ()
(gdb)
0x100109a4 in ?? ()
(gdb)
0x0ffd3884 in ?? () from /lib/ld.so.1 ----> 动态链接器解析 printf 符号,获得其函数实体地址,然后重写 0x100109c0 处的指令
(gdb)
b *0x1000047c
Breakpoint 2 at 0x1000047c: file test.c, line 14.
(gdb) c
Continuing.
f[0] = 0
Breakpoint 2, 0x1000047c in main () at test.c:14
14 in test.c
(gdb)
x /6i 0x100109c0
0x100109c0 <printf@plt>:
b 0xfeba210 <printf> ----> 第一次调用 printf 后,此处即被动态链接器 ld.so.1 重填
0x100109c4 <printf@plt+4>: b 0x10010988 <_GLOBAL_OFFSET_TABLE_+44>
0x100109c8 <printf@plt+8>: .long 0x0
0x100109cc <printf@plt+12>: .long 0x0
0x100109d0 <printf@plt+16>: .long 0x0
0x100109d4 <completed.6562>: .long 0x0
(gdb)
x /10i 0x10010988
0x10010988 <_GLOBAL_OFFSET_TABLE_+44>: rlwinm r12,r11,1,0,30
0x1001098c <_GLOBAL_OFFSET_TABLE_+48>: add r11,r12,r11
0x10010990 <_GLOBAL_OFFSET_TABLE_+52>: li r12,14468
0x10010994 <_GLOBAL_OFFSET_TABLE_+56>: addis r12,r12,4093
0x10010998 <_GLOBAL_OFFSET_TABLE_+60>: mtctr r12
0x1001099c <_GLOBAL_OFFSET_TABLE_+64>: li r12,0
0x100109a0 <_GLOBAL_OFFSET_TABLE_+68>: addis r12,r12,12288
0x100109a4 <_GLOBAL_OFFSET_TABLE_+72>: bctr
0x100109a8 <_GLOBAL_OFFSET_TABLE_+76>: .long 0x0
0x100109ac <_GLOBAL_OFFSET_TABLE_+80>: .long 0x0
PowerPC 下,动态链接的机制与 x86 不同之处在于:
PowerPC 上的 GOT 里是一些指令,第一次调用后,PLT 会被重填为新的指令。
而 x86 上 GOT 只是存放函数地址,起初是 PLT 里的下一条指令,第一次调用后会被修改为 printf 的地址,PLT 的内容不会被修改。