2011-06-10 93 views
2

我現在明白如何運作的動態引用,通過過程鏈接表如下圖所示:加載器引用共享庫中的變量是如何的?

Dump of assembler code for function [email protected]: 
0x0000000000400528 <[email protected]+0>: jmpq *0x2004d2(%rip)  # 0x600a00 <_GLOBAL_OFFSET_TABLE_+40> 
0x000000000040052e <[email protected]+6>: pushq $0x2 
0x0000000000400533 <[email protected]+11>: jmpq 0x4004f8 
(gdb) disas 0x4004f8 
No function contains specified address. 

但我不知道變量是如何動態引用,但我發現這些值填入一旦開始GOT,但是上面沒有存根,它是如何工作的?

回答

1

在將控制權轉移給用戶程序之前,動態加載程序會重新定位對變量的所有引用。

它們沒有「存根」,因爲一旦用戶程序開始執行,加載程序不可能重新獲得控制權並更新變量地址。如果這不清楚,那麼你還沒有真正的瞭解PLT懶惰分辨率存根的工作原理。

0

全局變量通過全局偏移表間接訪問。

  • 當編譯的程序時,編譯器會生成執行 間接訪問代碼,並且發出重定位信息指定在全局偏移表的條目 正在使用。
  • 鏈接器在創建最終的動態可加載對象 時會執行這些重定位,從而導致機器代碼不在 需要在加載時進一步修補。

要查看此操作,請考慮以下代碼片段。

int v1; 
int f(void) { return !v1; } 

函數f引用全局v1。機器代碼生成 該函數如下所示(在i386):

% gcc -c -fpic a.c 
% objdump --disassemble --reloc a.o 
[snip] 
Disassembly of section .text: 

00000000 <f>: 
    0: 55      push %ebp 
    1: 89 e5     mov %esp,%ebp 
    3: e8 fc ff ff ff   call 4 <f+0x4> 
      4: R_386_PC32 __i686.get_pc_thunk.cx 
    8: 81 c1 02 00 00 00  add $0x2,%ecx 
      a: R_386_GOTPC _GLOBAL_OFFSET_TABLE_ 
    e: 8b 81 00 00 00 00  mov 0x0(%ecx),%eax 
      10: R_386_GOT32 v1 
    14: 8b 00     mov (%eax),%eax 
    16: 85 c0     test %eax,%eax 
    18: 0f 94 c0    sete %al 
    1b: 0f b6 c0    movzbl %al,%eax 
    1e: 5d      pop %ebp 
    1f: c3      ret  

Disassembly of section .text.__i686.get_pc_thunk.cx: 

00000000 <__i686.get_pc_thunk.cx>: 
    0: 8b 0c 24    mov (%esp),%ecx 
    3: c3      ret 

機器碼步行通過:

  • (偏移爲0x0和0x1)標準函數序言。
  • (偏移0x3)__i686.get_pc_thunk.cx呼叫準備 PC相對由呼叫到寄存器%ecx後加載指令 的地址尋址。
  • (偏移量0x8)%ecx中的值調整爲指向全局偏移量表的起始 。該調整通過R_386_GOTPC類型的 重定位條目來指示。
  • (偏移量0xE)檢索全球v1的地址。 R_386_GOT32重新定位從 提供v1的條目偏移量作爲全局偏移量表的基數。
  • (偏移量0x14)v1中的值檢索到寄存器%eax中。
  • (偏移0x16--0x1F)函數的其餘部分計算f

在最終的共享對象中,接頭補丁的功能的代碼,以 以下:

% gcc -shared -o a.so a.o 
% objdump --disassemble a.so 
...snip... 
0000044c <f>: 
44c: 55      push %ebp 
44d: 89 e5     mov %esp,%ebp 
44f: e8 18 00 00 00   call 46c <__i686.get_pc_thunk.cx> 
454: 81 c1 a0 1b 00 00  add $0x1ba0,%ecx 
45a: 8b 81 f8 ff ff ff  mov -0x8(%ecx),%eax 
460: 8b 00     mov (%eax),%eax 
462: 85 c0     test %eax,%eax 
...snip... 
  • 假定對象被加載在抵消在存儲器Ó,所述 調用指令在偏移量0x44F將加載O + 0x454 + 0x1BA0,即, O + 0x1FF4到%ecx
  • 在偏移0x45A指令減去從8 %ecx 獲得時隙的地址v1在全局偏移表, 即,用於v1槽是在偏移0x1FEC從 共享對象的開始。

望着共享對象的動態重定位記錄,我們 看到一個重定位記錄,指示運行時加載器爲v1存儲 實際地址偏移量0x1FEC。

% objdump -R a.so 
DYNAMIC RELOCATION RECORDS 
OFFSET TYPE    VALUE 
...snip... 
00001fec R_386_GLOB_DAT v1 
...snip... 

延伸閱讀: