2011-03-29 36 views
49
0x00000000004004b6 <main+30>: callq 0x400398 <[email protected]> 

任何人都知道嗎?@plt是什麼意思?

UPDATE

爲什麼是兩個disas printf給我不同的結果?

(gdb) disas printf 
Dump of assembler code for function [email protected]: 
0x0000000000400398 <[email protected]+0>: jmpq *0x2004c2(%rip)  # 0x600860 <_GLOBAL_OFFSET_TABLE_+24> 
0x000000000040039e <[email protected]+6>: pushq $0x0 
0x00000000004003a3 <[email protected]+11>: jmpq 0x400388 

(gdb) disas printf 
Dump of assembler code for function printf: 
0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp 
0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp) 
0x00000037aa44d36c <printf+12>: movzbl %al,%edx 
0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp) 
0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax 
0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx  # 0x37aa44d3c2 <printf+98> 
+0

第一條輸出線從哪裏來? 'objdump'我想象? – 2015-05-31 20:37:37

回答

83

這是一種方式來獲得代碼調整,而無需維護代碼的單獨副本的每個處理(調整基於在碼坐在虛擬存儲器地址)。 PLT是過程鏈接表,這是使動態加載和鏈接更易於使用的結構之一。

[email protected]實際上是一個小的存根(最終)稱爲真正的printf函數。

這個實際功能可以映射到任意在給定進程(虛擬地址空間)中的位置,以及調用它的代碼。

因此,爲了允許正確的代碼共享調用代碼(左下方),您不希望直接對其應用任何修正,因爲這將限制它可以位於其他進程中的位置。

PLT處於可靠地計算-在運行時地址不是進程之間共享,所以任何給定的過程,但是可以更改其它要更小的過程特異性區域。

換句話說,檢查以下圖,它示出了兩個代碼,並在兩個過程映射到不同的虛擬地址的庫代碼:

  Mapped to: 0x1234  0x9000  0x8888 
     +-----------------+ +----------+ +----------+ 
     |     | | Private | |   | 
ProcA |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     | Shared   | +----------+ | Shared | 
========| application |==============| library |== 
     | code   | +----------+ | code | 
     |     | | Private | |   | 
ProcB |     | | PLT/GOT | |   | 
     |     | | area | |   | 
     +-----------------+ +----------+ +----------+ 
     Mapped to: 0x2020  0x9000  0x6666 

這個特殊的例子示出了一個簡單的情況下PLT映射到固定的位置。

<[email protected]+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24> 

一篇好文章可以發現here,詳細glibc是如何在運行時加載:在你的情況下,就證明你的程序計數器相關的查找它的位置相對於當前程序計數器。

基本上,創建共享代碼的原始方式意味着它必須加載到使用它的每個進程的虛擬地址空間中的相同內存位置。無論是它還是它都不能共享,因爲修復一個進程的共享副本的行爲將完全填滿另一個進程,並將其映射到不同的位置。

通過使用與位置無關的代碼,與PLT和全局偏移表(GOT),所述第一的函數的調用[email protected](在PLT)沿是一個多階段的操作,其中:

  • 您在PLT中致電[email protected]
  • 它調用GOT版本(通過指針),其中最初指向PLT中的某些設置代碼。
  • 這個設置代碼加載相關的共享庫,如果還沒有完成,則修改 GOT,以便後續調用直接到實際printf而不是設置代碼。

在隨後的調用,因爲GOT已被修改,多階段的方法被簡化:

  • 調用的PLT [email protected]
  • 它調用指向實數的GOT版本(通過指針)printf
+0

爲什麼兩個'disas printf'給我不同的結果? – gdb 2011-03-29 07:49:17

+1

存根是在我輸入'r'之前,另一個是在'break'之後 – gdb 2011-03-29 08:02:22

+0

它可以用gdb與任何可執行文件重現。 – gdb 2011-03-29 08:32:13

4

不確定,但可能你看到的是有道理的。第一次運行disas命令時,printf還沒有被調用,所以沒有解決。一旦程序在第一次更新GOT時調用printf方法,現在printf被解析並且GOT指向實際函數。因此,下一次調用disas命令會顯示真正的printf程序集。