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:
--no-pic
, so it is not PICfoo()
, 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.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.