【发布时间】:2021-04-09 18:23:38
【问题描述】:
考虑这段代码:
int foo();
int main() {
foo();
while(1){}
}
int foo() 在共享对象中实现。
用gcc -o main main.c -lfoo -nostdlib -m32 -O2 -e main --no-pic -L./shared 编译这段代码会得到以下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>
使用以下重定位:
$ 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
注意:
- 代码是用
--no-pic编译的,所以不是PIC - 在
.text部分(main函数)中对foo()的调用不通过 PLT。相反,它只是一个简单的R_386_PC32重定位,我假设它会在加载时直接重定位到foo函数的地址。这对我来说很有意义,因为代码不是 PIC,因此无需通过 PLT 添加额外的间接。 - 即使没有使用,PLT 仍在生成中。那里有一个
foo的条目,我们甚至有一个R_386_JUMP_SLOT重定位来在加载时(PLT 指向)在GOT 中设置foo条目。
我的问题很简单:我没有看到代码中的任何地方都使用了 PLT,我也没有看到这里有必要,那么为什么 gcc 会创建它?
【问题讨论】:
-
"我没有看到 PLT 在任何地方使用" --
foo()的定义 来自哪里?如果它来自libfoo.so(反汇编暗示),那么PLT 是必要的。 -
@EmployedRussian 是的,
foo()的定义来自libfoo.so。我不明白为什么在这种情况下需要 PLT。对foo()的调用未指向PLT。这是直接R_386_PC32搬迁。如果我用-fpic编译main.c,那么是的,我看到调用指向其相应的PLT 条目,然后我看到一个R_386_JUMP_SLOT重定位 -
另外,GOT 指针在调用
foo之前没有准备好。这似乎是未使用 PIC 编译模块时的行为。原始的main.o目标文件只准备GOT 指针并在使用-fpic编译时请求R_386_PLT32重定位。如果使用--no-pic编译,则不再设置GOT 指针,然后要求进行简单的R_386_PC32重定位,如问题所示。基于此,在我看来,在这种情况下,动态链接器只会将重定位直接添加到调用中,我不明白它将如何使用 PLT
标签: linux assembly x86 elf dynamic-linking