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:
- The code was compiled with
--no-pic, so it is not PIC - The call to
foo(), in the.textsection (mainfunction), is not going through the PLT. Instead, it is just a simpleR_386_PC32relocation that I assume it will directly be relocated to the address of thefoofunction 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. - Even without being used, the PLT is still being generated. An entry for
foois present there and we even have aR_386_JUMP_SLOTrelocation to set up thefooentry 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?