ARM elf で動的リンク関数を解析したい。ご存知のように、extern 関数 (fn1) を呼び出すと、関連する PLT 位置にジャンプし、その PLT コードは関連する GOT 位置からアドレスを取得します。fn1 が最初に呼び出された場合、GOT 内のこのアドレスは PLT セクションの開始オフセットであり、制御はそこにジャンプします (そして、すべての PLT fn は最初にここにジャンプします)、fn1 の実アドレスを解析するために、それを GOT に渡して呼び出します。その後、再度 fn1 を呼び出すと PLT にジャンプし、fn1 のアドレスは既に GOT にあるため、直接呼び出すことができます。
それは私の理解であり、gdbを使用してx86で正常に検証しています。しかし、ARM に切り替えると、様子がおかしいようです。PLT fn がアドレスを解析するために PLT セクションの開始オフセットにジャンプせず、GOT データが PLT セクションの開始オフセットでも、プログラム実行後の 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にブレークポイントを置いて実行しても全く壊れません!!! 私を助けてください...