先準備一份測試代碼:
#include <stdio.h>
void funcUp(void)
{
printf("Hello world 1!\n");
return;
}
int main(int argc, char* argv[])
{
funcUp();
funcDown();
return 0;
}
int funcDown(void)
{
printf("Hello world 2!\n");
return 0;
}
然後編譯並反彙編:
gcc
-g testElf.c -o test
objdump -S test
得到以下信息(部分):
可以看出在funcUp中調用printf時,彙編中顯示的是callq 4003f0 <puts@plt>,下面就來研究一下printf在進程空間中的地址。puts@plt相當於編譯器設定的中間函數,只有printf函數第一次被用到時才進行綁定(地址修正)。puts@plt對應的代碼:
用調試器進行分析:
gdb test
設置斷點,運行程序地址纔會綁定:
(gdb) break main
Breakpoint 1 at 0x400513: file testElf.c, line 12.
(gdb) run
Starting program: /home/administrator/develop/test/elf/test
Breakpoint 1, main (argc=1, argv=0x7fffffffdae8) at testElf.c:12
12 funcUp();
分析puts@plt,先跳到0x601018看看:
(gdb) x 0x601018
0x601018
<[email protected]>: 0x00000000004003f6
發現函數跳到了第二行,接着執行下去,到了第三行,查看0x4003e0代碼:
執行下去,來到GOT表偏移0x10(第5項)的位置:
(gdb) x 0x601010
0x601010: 0x00007ffff7def210
(gdb)disassemble 0x00007ffff7def210
Dump of assembler code for function _dl_runtime_resolve:
0x00007ffff7def210 <+0>: sub $0x38,%rsp
0x00007ffff7def214 <+4>: mov %rax,(%rsp)
0x00007ffff7def218 <+8>: mov %rcx,0x8(%rsp)
0x00007ffff7def21d <+13>: mov %rdx,0x10(%rsp)
0x00007ffff7def222 <+18>: mov %rsi,0x18(%rsp)
0x00007ffff7def227 <+23>: mov %rdi,0x20(%rsp)
0x00007ffff7def22c <+28>: mov %r8,0x28(%rsp)
0x00007ffff7def231 <+33>: mov %r9,0x30(%rsp)
0x00007ffff7def236 <+38>: mov 0x40(%rsp),%rsi
0x00007ffff7def23b <+43>: mov 0x38(%rsp),%rdi
0x00007ffff7def240 <+48>: callq 0x7ffff7de8690 <_dl_fixup>
0x00007ffff7def245 <+53>: mov %rax,%r11
0x00007ffff7def248 <+56>: mov 0x30(%rsp),%r9
0x00007ffff7def24d <+61>: mov 0x28(%rsp),%r8
0x00007ffff7def252 <+66>: mov 0x20(%rsp),%rdi
0x00007ffff7def257 <+71>: mov 0x18(%rsp),%rsi
0x00007ffff7def25c <+76>: mov 0x10(%rsp),%rdx
0x00007ffff7def261 <+81>: mov 0x8(%rsp),%rcx
0x00007ffff7def266 <+86>: mov (%rsp),%rax
0x00007ffff7def26a <+90>: add $0x48,%rsp
0x00007ffff7def26e <+94>: jmpq *%r11
End of assembler dump.
用disassemble命令反彙編指定函數得到上面的結果。可以看出程序最後跳轉到 _dl_runtime_resolve這個函數,這個函數是用來完成函數綁定工作的。
這時再看看puts@plt的第一條指令,跳到0x601018查看:
(gdb) x 0x601018
0x601018 <[email protected]>: loopne 0x600fd6
對比之前的內容。得出結論是,第一次執行時,通過_dl_runtime_resolve解析到函數地址,並保存puts的地址到0x601018裏,以後執行時就直接調用。
參考:http://www.cnblogs.com/xingyun/archive/2011/12/10/2283149.html