我想解析ARM elf中的动态链接函数。众所周知,当我们调用一个extern函数(fn1)时,它会跳转到相关的PLT位置,它的PLT代码会从相关的GOT位置获取地址。如果第一次调用fn1,GOT中这个地址就是PLT段的起始偏移,控制跳转到那里(每个PLT fn都会第一次跳转到这里),解析fn1的真实地址,把它到 GOT,然后调用它。而之后我们再次调用fn1的时候,它会跳转到PLT,而fn1的地址已经在GOT中了,所以我们可以直接调用它。
这是我的理解,我使用gdb在x86中验证成功。但是,当我切换到 ARM 时,事情似乎很奇怪。我发现PLT fn不是跳转到PLT section的起始偏移来解析地址,GOT数据不是PLT section的起始偏移,也不是程序运行后extern fn的地址。(但它包含运行前 PLT 部分的起始偏移量)
这是我在 GDB 中的调试设置:
1. 程序的反汇编视图
.PLT
===========================================================================
.plt:000083D0 AREA .plt, CODE
.plt:000083D0 STR LR, [SP,#var_4]!
.plt:000083D4 LDR LR, =(_GLOBAL_OFFSET_TABLE_ - 0x83E0)
.plt:000083D8 ADD LR, PC, LR
.plt:000083DC LDR PC, [LR,#8]!
.plt:000083DC ; ---------------------------------------------------------------------------
.plt:000083E0 off_83E0 DCD _GLOBAL_OFFSET_TABLE_ - 0x83E0 ;
.plt:000083E4 ; =============== S U B R O U T I N E =======================================
.plt:000083E4 sub_83E4
.plt:000083E4 ADRL R12, 0x83EC
.plt:000083EC LDR PC, [R12,#(off_90D4 - 0x83EC)]! ; sub_83D0
.plt:000083F0 ; =============== S U B R O U T I N E =======================================
.plt:000083F0 sub_83F0
.plt:000083F0 ADRL R12, 0x83F8
.plt:000083F8 LDR PC, [R12,#(off_90D8 - 0x83F8)]! ; sub_83D0
.GOT
===========================================================================
.got:000090C8 AREA .got, DATA
.got:000090C8 _GLOBAL_OFFSET_TABLE_ DCD 0 ; DATA XREF: sub_83D0+8
.got:000090CC DCD 0
.got:000090D0 DCD 0
.got:000090D4 off_90D4 DCD sub_83D0 ; DATA XREF: sub_83E4+8
.got:000090D8 off_90D8 DCD sub_83D0 ; DATA XREF: sub_83F0+8
.got:000090D8 ; .got ends
.TEXT
===========================================================================
.text:00008434 ADD R0, PC ; "okkkkkkk..."
.text:00008436 BLX sub_83F0
.text:0000843A POP {R4,PC}
2. 运行 arm-eabi-gdb
// start gdb
(gdb) target remote :5039
// before running, GOT's data is ok
(gdb) x/2x 0x90d4
0x90d4: 0x000083d0 0x000083d0
// PLT
(gdb) x/11i 0x83d0
0x83d0: push {lr} ; (str lr, [sp, #-4]!)
0x83d4: ldr lr, [pc, #4] ; 0x83e0
0x83d8: add lr, pc, lr
0x83dc: ldr pc, [lr, #8]!
0x83e0: andeq r0, r0, r8, ror #25
0x83e4: add r12, pc, #0
0x83e8: add r12, r12, #0
0x83ec: ldr pc, [r12, #3304]! ; 0xce8
0x83f0: add r12, pc, #0
0x83f4: add r12, r12, #0
0x83f8: ldr pc, [r12, #3296]! ; 0xce0
// set a breakpoint at the fn(printf)'s plt
(gdb) b *0x83f0
Breakpoint 1 at 0x83f0
// running, before call printf at first time, the GOT's data is?
(gdb) c
Continuing.
Breakpoint 1, 0x000083f0 in ?? ()
// strange...
(gdb) x/2x 0x90d4
0x90d4: 0xafd14ff9 0xafd19b81
(gdb) ni
0x000083f4 in ?? ()
(gdb) ni
0x000083f8 in ?? ()
(gdb) i r
...
r12 0x83f8 33784
sp 0xbe9b2c38 0xbe9b2c38
lr 0x843b 33851
pc 0x83f8 0x83f8
// after 'ni', printf is called and output a string...
// will return to 0x843a
(gdb) ni
0x0000843a in ?? ()
就这样。即使我在 0x83d0 处设置断点并运行,也没有完全中断!!!请帮我...