链接器如何解析在多个位置定义的全局符号()

在编译时, 编译器将每个全局符号以强或弱形式导出到汇编器, 并且汇编器在可重定位目标文件的符号表中隐式编码此信息。函数和初始化的全局变量会获得强符号。未初始化的全局变量会得到弱符号。
对于以下示例程序, buf, bufp0, main和swap是强符号; bufp1是一个弱符号。

/* main.c */ void swap(); int buf[2] = {1, 2}; int main() { swap(); return 0; }/* swap.c */ extern int buf[]; int *bufp0 = & buf[0]; int *bufp1; void swap() { int temp; bufp1 = & buf[1]; temp = *bufp0; *bufp0 = *bufp1; *bufp1 = temp; }

考虑到强符号和弱符号的概念, Unix链接器使用以下规则来处理多个定义的符号:
【链接器如何解析在多个位置定义的全局符号()】规则1:
不允许使用多个强符号(具有相同的变量名)。
规则2:
给定一个强符号和多个弱符号, 请选择强符号。
规则三:
给定多个弱符号, 请选择任意弱符号。
例如, 假设我们尝试编译并链接以下两个C模块:
/* foo1.c */ int main() { return 0; }/* bar1.c */ int main() { return 0; }

在这种情况下, 链接器将生成错误消息, 因为强符号main被多次定义(规则1):
$ gcc foo1.c bar1.c /tmp/cca015022.o: In function ‘main’: /tmp/cca015022.o(.text+0x0): multiple definition of ‘main’ /tmp/cca015021.o(.text+0x0): first defined here

同样, 链接器将为以下模块生成错误消息, 因为强符号x被定义了两次(规则1):
/* foo2.c */ int x = 15213; int main() { return 0; }/* bar2.c */ int x = 15213; void f() { }

但是, 如果x在一个模块中未初始化, 则链接器将安静地选择在另一个模块中定义的强符号(规则2), 如以下程序所示:
/* foo3.c */ #include < stdio.h> void f( void ); int x = 15213; int main() { f(); printf ( "x = %d\n" , x); return 0; }/* bar3.c */ int x; void f() { x = 15212; }

在运行时, 函数f()将x的值从15213更改为15212, 这对于函数main的作者可能是不受欢迎的!请注意, 链接器通常不表示已检测到x的多个定义。
$ gcc -o gfg foo3.c bar3.c $ ./gfg x = 15212

如果x的两个弱定义(, 规则3):
/*a.c*/ #include < stdio.h> void b( void ); int x; int main() { x = 2016; b(); printf ( "x = %d " , x); return 0; } /*b.c*/ #include < stdio.h> int x; void b() { x = 2017; }

规则2和3的适用:可能会引入一些难以理解的程序员难以理解的隐蔽运行时错误, 尤其是在重复的符号定义具有不同类型的情况下。
示例:” x” 在一个模块中定义为int, 在另一个模块中定义为double。
/*a.c*/ #include < stdio.h> void b( void ); int x = 2016; int y = 2017; int main() { b(); printf ( "x = 0x%x y = 0x%x \n" , x, y); return 0; } /*b.c*/ double x; void b() { x = -0.0; }

执行:
$ gcc a.c b.c -o srcmini $ ./srcmini x = 0x0 y = 0x80000000

这是一个微妙而令人讨厌的错误, 尤其是因为它是无提示地发生的, 没有来自编译系统的警告, 并且因为它通常在程序执行的更晚时显现出来, 并且远离发生错误的地方。在具有数百个模块的大型系统中, 此类错误极难修复, 尤其是因为许多程序员并不了解链接器的工作方式。如有疑问, 请使用诸如gcc -fno-common标志之类的标志调用链接器, 如果遇到多个已定义的全局符号, 该标志将触发错误。
资料来源:http://csapp.cs.cmu.edu/public/ch7-preview.pdf
如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。

    推荐阅读