今天閱讀ELF檔案結構中的GOT表與PLT表,生啪啪的文字沒看懂,索性從一個小實驗說起 ,一來記錄自己的技術成長軌迹,二來幫助需要之人。。
前言理論篇:
第一:為什麼要實作延遲綁定?
開始把所有的函數都連結實際是一種浪費,是以采用延遲綁定技術,核心是第一次用的時候進行綁定,沒有用到不進行綁定。
優點:加快程式的啟動速度
第二:怎麼實作上面的延遲綁定?(這一塊詳細介紹在程式員的自我修養這本書的7.4節)
使用PLT的方法,每個外部函數在PLT中都有一個相應的項,比如外部函數XXX為[email protected],實作為:
jmp “位址”
push “ printf引用在重定位表的“.rel.plt”中的下标”;
jump _dl_runtime_resolve//這個函數是完成符号解析和重定位的;
首先簡單的寫一個小程式:
<span style="font-size:24px;">#include <stdio.h>
int add(int a,int b)
{
int c=0;
c=a+b;
printf("%d\n",c);</span>
<span style="font-size:24px;"> return 0;
}
void main()
{
add(2,3);
}</span>
編 譯
gcc -g Got.c -o Got
反彙編看彙編代碼:
<span style="font-size:24px;">#include <stdio.h>
int add(int a,int b)
{
4004f4: 55 push %rbp
4004f5: 48 89 e5 mov %rsp,%rbp
4004f8: 48 83 ec 20 sub $0x20,%rsp
4004fc: 89 7d ec mov %edi,-0x14(%rbp)
4004ff: 89 75 e8 mov %esi,-0x18(%rbp)
int c=0;
400502: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
c=a+b;
400509: 8b 45 e8 mov -0x18(%rbp),%eax
40050c: 8b 55 ec mov -0x14(%rbp),%edx
40050f: 01 d0 add %edx,%eax
400511: 89 45 fc mov %eax,-0x4(%rbp)
printf("%d\n",c);
400514: b8 3c 06 40 00 mov $0x40063c,%eax
400519: 8b 55 fc mov -0x4(%rbp),%edx
40051c: 89 d6 mov %edx,%esi
40051e: 48 89 c7 mov %rax,%rdi
400521: b8 00 00 00 00 mov $0x0,%eax
400526: e8 c5 fe ff ff callq 4003f0 <[email protected]>
return 0;
40052b: b8 00 00 00 00 mov $0x0,%eax
}
400530: c9 leaveq
400531: c3 retq
0000000000400532 <main>:
void main()
{
400532: 55 push %rbp
400533: 48 89 e5 mov %rsp,%rbp
add(2,3);
400536: be 03 00 00 00 mov $0x3,%esi
40053b: bf 02 00 00 00 mov $0x2,%edi
400540: e8 af ff ff ff callq 4004f4 <add>
} </span>
</pre><span style="font-size:24px">我們看到主函數中調用子函數add是沒問題的,看到子函數中調用“400526:<span style="white-space:pre"></span>e8 c5 fe ff ff <span style="white-space:pre"></span>callq 4003f0 <[email protected]>”看到這個“[email protected]”這是一個延遲綁定,不是通過GOT中相應的項進行間接跳轉,而是通過PLT項來進行跳轉,每個外部函數在PLT中都有一個項,這裡是printf函數。并且[email protected]都有一個固定的結構:</span><p><span style="font-size:24px"><span style="background-color:rgb(240,240,240)">jmp “位址”</span></span></p><p><span style="font-size:24px"><span style="background-color:rgb(240,240,240)">push “ printf引用在重定位表的“.rel.plt”中的下标”;</span></span></p><p><span style="font-size:24px"><span style="background-color:rgb(240,240,240)">jump _dl_runtime_resolve//這個函數是完成符号解析和重定位的;</span></span></p><p><span style="font-size:24px; background-color:rgb(240,240,240)">繼續看一下</span><span style="font-size:24px; background-color:rgb(240,240,240)">[email protected]的彙編代碼:果然是這樣的套路:</span></p><p><span style="font-size:24px; background-color:rgb(240,240,240)"></span></p><pre name="code" class="plain"><span style="font-size:24px;">00000000004003f0 <[email protected]>:
4003f0: ff 25 0a 0c 20 00 jmpq *0x200c0a(%rip) # 601000 <_GLOBAL_OFFSET_TABLE_+0x18>
4003f6: 68 00 00 00 00 pushq $0x0
4003fb: e9 e0 ff ff ff jmpq 4003e0 <_init+0x18></span>
在這裡用gdb來調試一下:
很奇怪的事出現這個:

很納悶,對于我這種底層不是很懂的小白,之能首先懷疑是不是樣本的問題?
首先很肯定一點的不是樣本問題:歡迎各位大俠指正。
建議大家看一下這幾個部落格寫的比較好:
http://www.programlife.net/linux-got-plt.html;
http://blog.csdn.net/lmh12506/article/details/6801630;
總結篇:
總的來說:第一次其實是調用_dl_runtime_resolve進行GOT對應的表項的修改,以增加愛延遲綁定的目的,進而達到不用連結所用的函數,就是用到的時候再連結,當連結以後就直接從GOT調轉到真正的函數,進而增加效率。不要被生啪啪的理論文字吓到。