简体   繁体   English

为什么 gcc 在显然不需要时会生成 PLT?

[英]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:注意:

  1. The code was compiled with --no-pic , so it is not PIC代码是用--no-pic编译的,所以它不是 PIC
  2. The call to foo() , 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.这对我来说很有意义,因为代码不是 PIC,因此无需通过 PLT 添加额外的间接。
  3. Even without being used, the PLT is still being generated.即使没有使用,PLT 仍在生成中。 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).那里有一个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.

相关问题 为什么GCC为Cortex-A9生成Fxxx而不是Vxxx浮点汇编指令? - Why does GCC generates Fxxx instead of Vxxx floating point assembly instructions for Cortex-A9? 有PIT时为何需要TSC - Why TSC needed when there is PIT available gcc 生成具有执行权限的共享 object - gcc generates shared object with execute permissions Gcc链接器自动查找所需的额外库 - Gcc linker automagically finds needed extra libraries 为什么configure在安装GCC时说没有找到C编译器? - Why does configure say no C compiler found when GCC is installed? 为什么在编译Linux内核和uBoot时使用arm-linux-gnueabi-gcc而不是arm-none-eabi-gcc? - Why arm-linux-gnueabi-gcc and not arm-none-eabi-gcc when compiling Linux kernel and uBoot? 当我包含 atomic.h 时,GCC 说没有这样的文件,为什么? - when I include atomic.h, GCC says No such file, why? 为什么jQuery显然已停止在Firefox 22中工作? - Why has jQuery apparently stopped working in Firefox 22? GCC在编译时在/ usr / local / include中查找头文件,但在链接时不查找/ usr / local / lib中的库。为什么? - GCC looks for headers in /usr/local/include when compiling, but not for libraries in /usr/local/lib when linking. Why? 使用比平台提供的更新版本的GCC需要什么 - What is needed to use a newer version of GCC than the platform provides
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM