C++ 中链接器符号的一些问题
C++ 和 Java 中链接器符号的重整
C++ 和 Java 都允许重载方法,这些方法在源代码中有相同的名字,却有不同的参数列表。那么链接器是如何区别这些不同的重载函数之间的差异呢?
C++ 和 Java 中能使用重载函数,是因为编译器将每个唯一的方法和参数列表组合编码成一个对链接器来说唯一的名字。这种编码过程叫做重整(mangling),而相反的过程叫做恢复 (demangling) 。
幸运的是,C++ 和 Java 使用兼容的重整策略。
- 一个被重整的类名字是由名字中字符的整数数量,后面跟原始名字组成的。比如,类
Foo
被编码成3Foo
。 - 方法被编码为原始方法名,后面加上
__
,加上被重整的类名,再加上每个参数的单字母编码。比如,Foo::bar(int, long)
被编码为bar__3Fooil
。重整全局变量和模板名字的策略是相似的。
Linux 中,解析多重定义的全局符号
强/弱符号
- 函数和已初始化的全局变量是强符号
- 未初始化的全局变量是弱符号
根据强弱符号的定义,Linux 链接器使用下面的规则来处理多重定义的符号名:
- 规则 1:不允许有多个同名的强符号。
- 规则 2:如果有一个强符号和多个弱符号同名,那么选择强符号。
- 规则 3:如果有多个弱符号同名,那么从这些弱符号中任意选择一个。
下面是一个例子:
/* foo.c */
#include <stdio.h>
void f(void);
int y = 15212;
int x = 15213;
int main()
{
f();
printf("x=0x%x y=0x%x\n", x, y);
return 0;
}
/* bar.c */
double x;
void f()
{
x=-0.0;
}
在上面的例子中,输出结果如下,因为 double 为 8 个字节,而 int 为 4 个字节,因此赋值 x=-0.0
会覆盖内存中 x 和 y 的位置。
linux> ./foobar
x=0x0 y=0x80000000
参考资料:
- 《深入理解计算机系统》P471
评论区