[英]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. int foo()
在共享 object 中实现。
Compiling this code with gcc -o main main.c -lfoo -nostdlib -m32 -O2 -e main --no-pic -L./shared
gives the following diasm:使用
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>
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 PIC--no-pic
编译的,所以它不是 PICfoo()
, in the .text
section ( main
function), is not going through the PLT..text
部分( main
函数)中对foo()
的调用没有通过 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.R_386_PC32
重定位,我假设它将在加载时直接重定位到foo
function 的地址。 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).foo
条目,我们甚至有一个R_386_JUMP_SLOT
重定位来在加载时(PLT 指向的)在 GOT 中设置foo
条目。 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?我的问题很简单:我没有看到代码中的任何地方都使用了 PLT,我也认为这里没有必要,那么为什么 gcc 会创建它?
--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. --no-pic
不像-no-pie
,它似乎是-fno-pic
或-fno-pie
影响代码生成但不链接的同义词。 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
.假设您的发行版的 GCC 默认制作一个 PIE,那么您正在制作一个 PIE,因此没有转换到
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
.我收到 linker 警告
/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.) (但可执行文件确实运行,不像它是 64 位,其中
call rel32
不能重定位到整个地址空间。)
And yeah, there is an unused PLT entry built by ld
for some reason, but the way you're linking is totally nonstandard.是的,由于某种原因,
ld
构建了一个未使用的 PLT 条目,但是您链接的方式完全是非标准的。
The normal reason for building a PLT is:建立 PLT 的正常原因是:
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. ld
链接非 PIE 时,会将call foo
转换为call foo@plt
,而不是在每次程序加载时都需要运行时修复的每个调用点包含文本重定位。
Use -fno-plt
to get more efficient asm, especially for 64-bit mode where even PIE code can efficiently reference the GOT directly.使用
-fno-plt
可以获得更高效的 asm,尤其是对于 64 位模式,即使 PIE 代码也可以直接有效地引用 GOT。
To make a simpler example, I used a function in libc ( getpid
) instead of a custom library.为了举一个更简单的例子,我在 libc (
getpid
) 中使用了 function 而不是自定义库。 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>
.使用
gcc -fno-pie -no-pie -m32 -O2 foo.c
正常编译,我得到 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
.但是添加
-fno-plt
,我得到 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.不涉及 PLT,只是用绝对地址引用的 GOT 条目。
No runtime relocation needed;无需运行时重定位; this page of the
.text
section can stay "clean" as a file-backed private mapping of the executable. .text
部分的这个页面可以作为可执行文件的文件支持的私有映射保持“干净”。 (Runtime relocation would dirty it, making it backed only by swap space if the kernel wanted to evict that page.) (运行时重定位会弄脏它,如果 kernel 想要驱逐该页面,则使其仅由交换空间支持。)
Also, it uses a "normal" GOT entry which needs early binding.此外,它使用需要早期绑定的“普通”GOT 条目。 This does work even with
-nostdlib -lc
and the ill-advised -e main
instead of calling it _start
like a normal person.即使使用
-nostdlib -lc
和不明智的-e main
而不是像普通人那样调用它_start
,这也确实有效。 Since it's a dynamically linked executable, the dynamic linker does run before your entry point and set up the GOT.由于它是一个动态链接的可执行文件,动态 linker 确实在您的入口点之前运行并设置 GOT。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.