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

    在编译时,编译器将每个全局符号作为强符号或弱符号导出到汇编程序,汇编程序在可重定位对象文件的符号表中隐式地编码这些信息。函数和初始化的全局变量得到强符号。未初始化的全局变量得到弱符号。
    对于以下示例程序,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:给定一个强符号和多个弱符号,选择强符号。
    规则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\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 geeksforgeeks
$ ./geeksforgeeks
x = 0x0 y = 0x80000000

    这是一个微妙而令人讨厌的错误,尤其是因为它是无提示地发生的,没有来自编译系统的警告,并且因为它通常在程序执行的更晚时表现出来,并且远离发生错误的地方。在具有数百个模块的大型系统中,此类错误非常难以修复,尤其是因为许多程序员并不了解链接器的工作方式。如有疑问,请使用诸如gcc -fno-common标志之类的标志调用链接器,如果遇到多个已定义的全局符号,该标志将触发错误。

参考文档

[1]Sahil Rajput.How Linkers Resolve Global Symbols Defined at Multiple Places?[EB/OL].https://www.geeksforgeeks.org/how-linkers-resolve-multiply-defined-global-symbols/,2019-01-04.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值