当您在
ELF格式中编译.o文件时,您的.o文件上有许多内容,例如:
- 包含代码的.text部分;
- 包含全局变量的.data、.rodata、.rss部分;
- 包含符号列表(函数、全局变量和其他)及其在文件中的位置以及.o文件使用的符号的.symtab;
- 诸如.rela.text之类的部分,这些部分是重定位列表--这些是链接编辑器(和/或动态链接器)必须进行的修改,以便将程序的不同部分链接在一起。
在调用方
让我们编译一个简单的C文件:
extern void GrCircleDraw(int x);
int foo()
{
GrCircleDraw(42);
return 3;
}
int bla()
{
return 2;
}
使用:
gcc -o test.o test.c -c
我正在使用系统的本地编译器,但当交叉编译到ARM时,它也可以正常工作。
您可以使用以下命令查看.o文件的内容:
readelf -a test.o
在符号表中,您将找到以下内容:
符号表'.symtab'包含10个条目:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
[...]
8: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 foo
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND GrCircleDraw
10: 0000000000000015 11 FUNC GLOBAL DEFAULT 1 bla
我们的foo
函数和bla
函数各有一个符号。值字段给出它们在.text
部分中的位置。
对于使用的符号GrCircleDraw
,有一个符号:它未定义,因为该函数未在此.o
文件中定义,但仍需在其他地方找到。
在.text
部分的重定位表(.rela.text
)中,您会发现:
重定位段'.rela.text'的偏移量为0x260,包含1个条目:
偏移量 信息 类型 符号值 符号名 + 加数
00000000000a 000900000002 R_X86_64_PC32 0000000000000000 GrCircleDraw - 4
这个地址在foo
内部:链接编辑器将会用GrCircleDraw
函数的地址来修补这个地址处的指令。
被调用方实现
现在让我们编译一个GrCircleDraw
的实现:
void GrCircleDraw(int x)
{
}
让我们来看一下它的符号表:
符号表'.symtab'包含9个条目:
Num: Value Size Type Bind Vis Ndx Name
[...]
8: 0000000000000000 9 FUNC GLOBAL DEFAULT 1 GrCircleDraw
它有一个条目GrCircleDraw
,定义了它在.text
部分中的位置。
将它们链接在一起
因此,当链接编辑器将两个文件组合在一起时,它知道:
- 哪些函数是在哪个
.o
文件中定义的以及它们的位置;
- 调用者代码中必须更新为被调用者地址的位置。