我从程序员的角度阅读计算机系统,这是关于链接的章节。它说明了如何使用程序ld在Linux x86-64中进行链接。作者声称,为了从可重定位的目标文件构建可执行文件,链接器执行两件事:符号解析和重定位。这是他们对什么是符号解析的简要概述:
目标文件定义和引用符号,其中每个符号对应于一个函数,一个全局变量或一个静态变量(即,用static属性声明的任何C变量)。符号解析的目的是将每个符号引用与一个符号定义完全关联。
但是,即使他们开始深入描述符号分辨率,也无法弄清楚符号引用的含义。那么可重定位目标文件中引用的符号究竟有多精确?
考虑以下来源:
static int foo() { return 42; }
static int bar() { return foo() + 1; }
extern int baz();
int main()
{
return foo() + bar() + baz();
}
在之后gcc -c foo.c
,objdump -d foo.o
x86_64 Linux上的输出是:
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <foo>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: b8 2a 00 00 00 mov $0x2a,%eax
9: 5d pop %rbp
a: c3 retq
000000000000000b <bar>:
b: 55 push %rbp
c: 48 89 e5 mov %rsp,%rbp
f: b8 00 00 00 00 mov $0x0,%eax
14: e8 e7 ff ff ff callq 0 <foo>
19: 83 c0 01 add $0x1,%eax
1c: 5d pop %rbp
1d: c3 retq
000000000000001e <main>:
1e: 55 push %rbp
1f: 48 89 e5 mov %rsp,%rbp
22: 53 push %rbx
23: 48 83 ec 08 sub $0x8,%rsp
27: b8 00 00 00 00 mov $0x0,%eax
2c: e8 cf ff ff ff callq 0 <foo>
31: 89 c3 mov %eax,%ebx
33: b8 00 00 00 00 mov $0x0,%eax
38: e8 ce ff ff ff callq b <bar>
3d: 01 c3 add %eax,%ebx
3f: b8 00 00 00 00 mov $0x0,%eax
44: e8 00 00 00 00 callq 49 <main+0x2b>
49: 01 d8 add %ebx,%eax
4b: 48 83 c4 08 add $0x8,%rsp
4f: 5b pop %rbx
50: 5d pop %rbp
51: c3 retq
这里有几件事要注意:
bar
呼叫方式?怎么知道那是被叫?真的可以在地址0吗?(大多数现代系统都使用来映射虚拟内存的零页,因此那里没有读写访问权限。)foo
0
objdump
foo
PROT_NONE
baz
从main
是从调用不同的foo
和bar
?编译器知道调用指令本身的位置foo
和bar
位置,但不知道调用位置baz
。因此,根据上述信息,链接器如何将其转变为明智的选择?不能:这里没有足够的信息。
为了使链接器能够将对baz
(我们尚未看到的)引用链接到的调用中baz
,它需要其他信息。在ELF系统上,该附加信息被写入.rela.text
此处的特殊部分,其中包括:
$ readelf -Wr foo.o
Relocation section '.rela.text' at offset 0x5d0 contains 1 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000045 0000000b00000002 R_X86_64_PC32 0000000000000000 baz - 4
那是书中谈到但没有定义的“参考”。它告诉链接:如果可以找到baz
(在其他对象中)一个定义,则获取其地址,然后将其放入(实际上,&baz - 4
因为该CALL
指令是相对于指令之后的下一条指令而言CALL
)放入该.text
节的字节[45-48]中的foo.o
。
如果没有这样的定义?链接器将产生错误:
$ gcc foo.o
foo.o: In function `main':
foo.c:(.text+0x45): undefined reference to `baz'
collect2: error: ld returned 1 exit status
最后,到达以上第1点:foo
真的可以在地址0处吗?
不,但是CALL
地址0x14
上的指令实际上没有说CALL 0
。它说:“在调用之后的下一条指令的地址处,减去25,调用例程”。如果在最终的二进制端向上在地址调用指令0x400501
,则该呼叫的目标将是0x4004ed
,这是在foo
将结束(之间的距离foo
和CALL
将不会改变,当链接程序重定位.text
的部分foo.o
到不同的地址(接头松弛尽管如此;但这又是一个复杂的话题)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句