考虑以下代码:
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
重定位来设置加载时 GOT 中的foo
条目(PLT 指向该条目)。
foo()
的定义是从哪里来的?如果它来自于libfoo.so
(如反汇编所示),那么 PLT 是必要的。 - undefinedfoo()
的定义来自libfoo.so
。我不明白为什么在这种情况下需要 PLT。对foo()
的调用并不指向 PLT,而是直接使用了R_386_PC32
重定位。如果我使用-fpic
编译main.c
,那么是的,我会看到调用指向相应的 PLT 条目,然后我会看到一个单独的R_386_JUMP_SLOT
重定位。 - undefinedfoo
之前,GOT指针没有被准备好。这似乎是在模块未使用PIC编译时的行为。原始的main.o
目标文件只在使用-fpic
编译时准备GOT指针并请求R_386_PLT32
重定位。如果使用--no-pic
编译,则不再设置GOT指针,并且会请求一个简单的R_386_PC32
重定位,如问题所示。基于此,我认为动态链接器将直接将重定位添加到此情况下的调用中,我不明白它是如何使用PLT的。 - undefined