符号与链接的一些细节
一. 符号修饰与函数签名
背景:20世纪70年代,编译器编译源代码产生目标文件时,符号名与相应的变量名或函数名相同。比如一个汇编代码里面包含一个函数foo,那么汇编器将它编译成目标文件以后,foo在目标文件中的相对应的符号也是foo。当后来UNIX平台和C语言发明时,已经存在了相当多的汇编编写的库和目标文件。那么当C语言想用汇编的库时,自己就不能再定义同名的函数。
为了防止命名冲突,UNIX下规定,C语言源代码文件中的所有全局变量和函数经过编译后,相对应的符号名前加上下划线"_"。
- C++符号修饰
C++允许函数重载,也支持命名空间,可以存在多个同样名字的符号。那么C++是如何管理同名函数的符号呢?
例:
int func(int);
float func(float);
class C{
int func(int);
class C2{
int func(int);
};
};
namespace N{
int func(int);
class C{
int func(int);
};
}
函数签名 | 修饰后名称(符号名) |
---|---|
int func(int) | _Z4funci |
int N::C::func(int) | _ZN1N1C4funcEi |
函数签名包含了一个函数的信息,包括函数名、参数类型、所在的类和命名空间及其他信息,用于识别不同的函数。在编译器和连接器处理符号的时候,它们使用某种名称修饰法,使得每个函数签名对应一个修饰后名称。表格中给出了GCC编译中的表示方法。
-
external “C”
当C++想调用C的库时,如果继续使用C++的名称修饰机制,显然无法成功链接C的符号。加上extern “C”后,其中的代码会被当作C语言编译。 -
强符号和弱符号
对于C/C++来说,编译器默认函数和初始化了的全局变量是强符号,未初始化的全局变量为弱符号。GCC中,可以通过"__attribute–((weak))",来定义任何一个强符号为弱符号。
针对强弱符号的概念,连接器会按如下规则处理与选择被多次定义的全局符号:
规则1:不允许强符号被多次定义;如果有多个强符号,连接器报错。
规则2:如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,则选择强符号。
规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。 -
强引用和弱引用
如果没有找到一个符号的定义,连接器就报未定义错误,那么这种引用被称为强引用。与之对应的是弱引用,如果弱引用为定义,连接器不报错,默认其为0,或者是一个特殊的值。可以通过关键字“__attribute–((weakref))”来声明一个外部符号为弱引用。
弱符号和弱引用对于库来说十分有用比如库中定义的弱符号可以被用户定义的强符号所覆盖,从而可以使得程序可以使用自定义版本库函数;或者程序可以对某些扩展功能模块的引用定义为弱引用,可以使得程序的功能个更容易裁剪和组合。
上一篇: Ubuntu添加用户未指定shell,ll别名等无法使用
下一篇: 极光推送设置别名和标签