简体   繁体   中英

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

Consider this code:

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

int foo() is implemented in a shared object.

Compiling this code with gcc -o main main.c -lfoo -nostdlib -m32 -O2 -e main --no-pic -L./shared gives the following diasm:

$ 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>

With the following relocations:

$ 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

Note that:

  1. The code was compiled with --no-pic , so it is not PIC
  2. The call to foo() , in the .text section ( main function), is not going through the PLT. Instead, it is just a simple R_386_PC32 relocation that I assume it will directly be relocated to the address of the foo function at load time. It makes sense to me since the code is not PIC, so there is no need to add an extra indirection through the PLT.
  3. Even without being used, the PLT is still being generated. An entry for foo is present there and we even have a R_386_JUMP_SLOT relocation to set up the foo entry in the GOT at load time (which the PLT points to).

My question is simple: I don't see the PLT being used anywhere in the code and I also don't see it being necessary here, so why does gcc creates it?

--no-pic isn't like -no-pie , it seems to be a synonym for -fno-pic or -fno-pie affecting code-gen but not linking. Assuming your distro's GCC defaults to making a PIE, you are making a PIE so there's no conversion of the call to foo@plt .

I get a linker warning /tmp/ccyRsNtd.o: warning: relocation against 'getpid@@GLIBC_2.0' in read-only section '.text.startup' / warning: creating DT_TEXTREL in a PIE . (But the executable does run, unlike if it were 64-bit where call rel32 isn't relocatable to the whole address space.)

And yeah, there is an unused PLT entry built by ld for some reason, but the way you're linking is totally nonstandard.


The normal reason for building a PLT is:

ld when linking a non-PIE will convert call foo into call foo@plt instead of including text relocations at every callsite that would require runtime fixups every time the program loads.

Use -fno-plt to get more efficient asm, especially for 64-bit mode where even PIE code can efficiently reference the GOT directly.

To make a simpler example, I used a function in libc ( getpid ) instead of a custom library. Compiling normally with gcc -fno-pie -no-pie -m32 -O2 foo.c , I get 5-byte e8 d5 ff ff ff call rel32: call 8049040 <getpid@plt> .

But adding -fno-plt to that, I get 6-byte ff 15 f4 bf 04 08 call [disp32] - call DWORD PTR ds:0x804bff4 . No PLT involved, just the GOT entry referenced with an absolute address.

No runtime relocation needed; this page of the .text section can stay "clean" as a file-backed private mapping of the executable. (Runtime relocation would dirty it, making it backed only by swap space if the kernel wanted to evict that page.)

Also, it uses a "normal" GOT entry which needs early binding. This does work even with -nostdlib -lc and the ill-advised -e main instead of calling it _start like a normal person. Since it's a dynamically linked executable, the dynamic linker does run before your entry point and set up the GOT.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM