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

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

null

/* 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: 给定一个强符号和多个弱符号,选择强符号。 规则3: 给定多个弱符号,请选择任意一个弱符号。 例如,假设我们试图编译并链接以下两个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" , 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 " , x, y);
return 0;
}
/*b.c*/
double x;
void b()
{
x = -0.0;
}


执行:

$ gcc a.c b.c -o geeksforgeeks
$ ./geeksforgeeks
x = 0x0 y = 0x80000000

这是一个微妙而令人讨厌的错误,尤其是因为它在没有来自编译系统的警告的情况下悄无声息地发生,并且因为它通常在程序执行的很晚的时候出现,远离错误发生的地方。在一个包含数百个模块的大型系统中,此类错误极难修复,尤其是因为许多程序员不知道链接器是如何工作的。如果有疑问,可以使用gcc-fno公共标志等标志调用链接器,如果遇到多个定义的全局符号,就会触发错误。

资料来源:http://csapp.cs.cmu.edu/public/ch7-preview.pdf

本文由 萨希尔·拉吉普特 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享