当您以ELF 格式编译 .o 文件时,文件中有许多内容,.o
例如:
.text
包含代码的部分;
.data
, .rodata
,.rss
包含全局变量的部分;
- a
.symtab
包含.o
(以及它们在文件中的位置)中的符号(函数、全局变量和其他)列表以及文件使用的符号.o
;
- 诸如
.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:值大小类型绑定 Vis Ndx 名称
0: 0000000000000000 0 NOTYPE 本地默认值
[...]
8: 0000000000000000 21 FUNC 全局默认值 1 foo
9: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND GrCircleDraw
10: 0000000000000015 11 FUNC 全局默认值 1 bla
我们的foo
函数有一个符号,一个bla
. 值字段给出了它们在该.text
部分中的位置。
使用的符号有一个符号GrCircleDraw
:它是未定义的,因为这个函数没有在这个.o
文件中定义,但仍然可以在其他地方找到。
在.text
( .rela.text
) 部分的重定位表中,您可以找到:
偏移 0x260 处的重定位节“.rela.text”包含 1 个条目:
偏移信息类型 Sym。价值符号。姓名+加号
00000000000a 000900000002 R_X86_64_PC32 0000000000000000 GrCircleDraw - 4
此地址在 内foo
:链接编辑器将用函数的地址修补此地址处的指令GrCircleDraw
。
在被调用方
现在让我们自己编译一个实现GrCircleDraw
:
void GrCircleDraw(int x)
{
}
让我们看一下它的符号表:
符号表 '.symtab' 包含 9 个条目:
Num:值大小类型绑定 Vis Ndx 名称
[...]
8: 0000000000000000 9 FUNC 全局默认值 1 GrCircleDraw
它有一个条目用于GrCircleDraw
定义其在其.text
部分中的位置。
将它们连接在一起
因此,当链接编辑器将两个文件组合在一起时,它知道:
- 在哪个
.o
文件中定义了哪些函数及其位置;
- 在调用者的代码中,它必须使用被调用者的地址进行更新。