全局變量通過全局偏移表間接訪問。
- 當編譯的程序時,編譯器會生成執行 間接訪問代碼,並且發出重定位信息指定在全局偏移表的條目 正在使用。
- 鏈接器在創建最終的動態可加載對象 時會執行這些重定位,從而導致機器代碼不在 需要在加載時進一步修補。
要查看此操作,請考慮以下代碼片段。
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...
延伸閱讀: