链接器如何知道外部函数的定义在哪里
How does the linker know where is the definition of an extern function?
我读了几篇文章,得出结论认为extern告诉编译器"这个函数存在,但它的代码在其他地方。不要恐慌。"但链接器如何知道函数定义在哪里。
我的案例:-我正在制作凯尔uvision 4。有一个头文件grlib.h,主函数在grlib_demo.c中(它包括grlib.h)。现在,有一个函数GrCircleDraw(),它在Circle.c中定义并在grlib.demo.c中调用,还有一个语句
extern void GrCircleDraw(所有参数);
grlib.h中的
我的问题是链接器如何知道GrCircleDraw()的定义在哪里,因为Circle.c不包括在grlib.h和grlib_demo.c 中
注意:-文件grlib.h和Circle.c在同一个文件夹中。代码运行成功。
以ELF格式编译.o文件时,.o
文件上有许多内容,例如:
- 包含代码的
.text
部分 - 包含全局变量的
.data
、.rodata
、.rss
部分 .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:0000000000000000000000000未键入本地默认UND[…]8:000000000000000021 FUNC全球默认值1 foo9:0000000000000000000000000 NOTYPE全局默认UND GrCircleDraw10:0000000000000015 11 FUNC GLOBAL DEFAULT 1 bla
我们的foo
函数和bla
函数各有一个符号。值字段给出了它们在.text
部分中的位置。
使用的符号GrCircleDraw
有一个符号:它是未定义的,因为此函数未在此.o
文件中定义,但仍有待在其他地方找到。
在.text
部分(.rela.text
)的重新定位表中,您可以找到:
偏移量0x260处的重定位部分'.rea.text'包含1个条目:偏移信息类型符号。Value Sym。名称+地址00000000000 a 00090000002R_X86_64_PC32 0000000000000000GrCircleDraw-4
此地址在foo
中:链接编辑器将用GrCircleDraw
函数的地址修补此地址处的指令。
在被叫方
现在让我们自己编译一个GrCircleDraw
:的实现
void GrCircleDraw(int x)
{
}
让我们看看它的符号表:
符号表".symtab"包含9个条目:Num:值大小类型绑定Vis-Ndx名称[…]8:0000000000000000000000009 FUNC GLOBAL DEFAULT 1 GrCircleDraw
它有一个GrCircleDraw
条目,用于定义其在.text
部分中的位置。
将它们连接在一起
因此,当链接编辑器将两个文件组合在一起时,它就知道:
- 在哪个
.o
文件中定义了哪些函数及其位置 - 其中,在调用者的代码中,它必须使用被调用者的地址进行更新
简单的答案是"编译器不需要知道,但链接器必须能够找到它"。通过多个.o
文件或库,链接器必须能够找到GrCircleDraw
函数的单个定义。
编译器只将extern
函数的名称放入.obj
文件中。编译器不需要了解更多信息。
当您开始链接时,作为开发人员,您有责任将所有必要的对象文件和库文件提供给链接器。Linker会将所有这些函数排列成二进制。如果没有指定正确的库或.obj
文件,则使用unresolved blah-blah
链接将失败。
默认库通常是隐式包含的。这会使事情复杂化并产生幻觉。您可以始终指定不需要任何隐式库,并显式包含所有内容。不幸的是,每个系统都有自己的方式。
链接通常是这样发生的:命令行被迭代,给出的每个参数都是
如果是对象文件,则直接使用- 用于所需的范围(=完成迄今为止未解决的所有引用)
最后,必须满足每个引用才能成功链接。链接器命令行中给出的行顺序很重要。
- 未定义的引用在哪里
- C++ 中的自定义异常:在哪里定义它们?
- 在标准中,模板参数的语法在哪里定义,例如,'std::function<int(char)>'?
- 具体来说,标准在哪里规定修改 const 对象是未定义的行为?
- CLion 是否支持自定义输入?如果是,我在哪里输入它们?
- 在C++中在哪里定义类常量?
- 存储在 C++ 编译器中的标识符的定义(不是声明)在哪里?
- size_t在哪里定义
- 标准在哪里定义了可变变量可以改变
- 标准在哪里指定类中定义的函数是内联的?
- libc++ 的定义在哪里ios_base::clear
- 链接器如何知道外部函数的定义在哪里
- “结构ap_conf_vector_t”的定义在哪里
- 在C++中,std::cin 对象定义在哪里
- GL_UNSIGNED_INT_8_8_8_8_REV定义在哪里
- LPPICTURE定义在哪里?
- c++ 11标准中“顶级cv限定符”的定义在哪里?
- V8::Isolate定义在哪里?
- '$OutDir'在Visual Studio的脚本文件中定义在哪里?
- OpenCV SVM 教程?Mat_<float>(1,2) 的函数定义在哪里