簡體   English   中英

為什么 gcc 在顯然不需要時會生成 PLT?

[英]Why gcc generates a PLT when it is apparently not needed?

考慮這段代碼:

int foo();
int main() {
    foo();
    while(1){}
}

int foo()在共享 object 中實現。

使用gcc -o main main.c -lfoo -nostdlib -m32 -O2 -e main --no-pic -L./shared編譯此代碼會得到以下結果:

$ objdump -d ./main

./main:     file format elf32-i386


Disassembly of section .plt:

00000240 <.plt>:
 240:   ff b3 04 00 00 00       pushl  0x4(%ebx)
 246:   ff a3 08 00 00 00       jmp    *0x8(%ebx)
 24c:   00 00                   add    %al,(%eax)
    ...

00000250 <foo@plt>:
 250:   ff a3 0c 00 00 00       jmp    *0xc(%ebx)
 256:   68 00 00 00 00          push   $0x0
 25b:   e9 e0 ff ff ff          jmp    240 <.plt>

Disassembly of section .text:

00000260 <main>:
 260:   8d 4c 24 04             lea    0x4(%esp),%ecx
 264:   83 e4 f0                and    $0xfffffff0,%esp
 267:   ff 71 fc                pushl  -0x4(%ecx)
 26a:   55                      push   %ebp
 26b:   89 e5                   mov    %esp,%ebp
 26d:   51                      push   %ecx
 26e:   83 ec 04                sub    $0x4,%esp
 271:   e8 fc ff ff ff          call   272 <main+0x12>
 276:   eb fe                   jmp    276 <main+0x16>

通過以下搬遷:

$ objdump -R ./main

./main:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
00000272 R_386_PC32        foo
00001ffc R_386_JUMP_SLOT   foo

注意:

  1. 代碼是用--no-pic編譯的,所以它不是 PIC
  2. .text部分( main函數)中對foo()的調用沒有通過 PLT。 相反,它只是一個簡單的R_386_PC32重定位,我假設它將在加載時直接重定位到foo function 的地址。 這對我來說很有意義,因為代碼不是 PIC,因此無需通過 PLT 添加額外的間接。
  3. 即使沒有使用,PLT 仍在生成中。 那里有一個foo條目,我們甚至有一個R_386_JUMP_SLOT重定位來在加載時(PLT 指向的)在 GOT 中設置foo條目。

我的問題很簡單:我沒有看到代碼中的任何地方都使用了 PLT,我也認為這里沒有必要,那么為什么 gcc 會創建它?

--no-pic不像-no-pie ,它似乎是-fno-pic-fno-pie影響代碼生成但不鏈接的同義詞。 假設您的發行版的 GCC 默認制作一個 PIE,那么您正在制作一個 PIE,因此沒有轉換到foo@plt的調用。

我收到 linker 警告/tmp/ccyRsNtd.o: warning: relocation against 'getpid@@GLIBC_2.0' in read-only section '.text.startup' / warning: creating DT_TEXTREL in a PIE (但可執行文件確實運行,不像它是 64 位,其中call rel32不能重定位到整個地址空間。)

是的,由於某種原因, ld構建了一個未使用的 PLT 條目,但是您鏈接的方式完全是非標准的。


建立 PLT 的正常原因是:

ld鏈接非 PIE 時,會將call foo轉換為call foo@plt ,而不是在每次程序加載時都需要運行時修復的每個調用點包含文本重定位。

使用-fno-plt可以獲得更高效的 asm,尤其是對於 64 位模式,即使 PIE 代碼也可以直接有效地引用 GOT。

為了舉一個更簡單的例子,我在 libc ( getpid ) 中使用了 function 而不是自定義庫。 使用gcc -fno-pie -no-pie -m32 -O2 foo.c正常編譯,我得到 5-byte e8 d5 ff ff ff call rel32: call 8049040 <getpid@plt>

但是添加-fno-plt ,我得到 6-byte ff 15 f4 bf 04 08 call [disp32] - call DWORD PTR ds:0x804bff4 不涉及 PLT,只是用絕對地址引用的 GOT 條目。

無需運行時重定位; .text部分的這個頁面可以作為可執行文件的文件支持的私有映射保持“干凈”。 (運行時重定位會弄臟它,如果 kernel 想要驅逐該頁面,則使其僅由交換空間支持。)

此外,它使用需要早期綁定的“普通”GOT 條目。 即使使用-nostdlib -lc和不明智的-e main而不是像普通人那樣調用它_start ,這也確實有效。 由於它是一個動態鏈接的可執行文件,動態 linker 確實在您的入口點之前運行並設置 GOT。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM