举例讲解C语言链接器的符号解析机制
1. 符号分类 // in lib1.c int x; void f() { x = 1235; } // in main1.c #include<stdio.h> void f(void); int x = 1234; int main(void) { f(); printf("x=%dn",x); return 0; } 上面的代码中,main函数printf输出: x=1235。因为链接器通过规则2决议符号x的定义为main.c中的强符号定义,而lib.c的作者并不知情,他对x的使用和修改影响到了main.c。这种交互修改,相互影响将会很复杂,因为大家都以为自己在做对的事情,在用对的变量。而整个决议过程,链接器悄无声息地完成了。 // in lib2.c double x; void f() { x = -0.0; } // in main2.c #include<stdio.h> void f(void); int x = 1234; int y = 1235; int main() { f(); printf("x=0x%x y=0x%x n",x,y); return 0; } 这种情况下,程序得到输出: x=0x0 y=0x80000000,而链接器(gcc ld)也终于给出一条警告: 复制代码 代码如下: ld: warning: tentative definition of '_x' with size 8 from 'obj/Debug/lib2.o' is being replaced by real definition of smaller size 4 from 'obj/Debug/main2.o' 链接器决议的是符号地址,而相邻的全局变量可能在.data段中的内存地址也相邻,因此也就引发了更复杂的问题。这一点和栈溢出很像,但是比栈溢出更复杂,因为问题出在多个模块之间,而不是在一个函数内部。 例3: // in lib3.c struct { int a; int b; } x; void f() { x.a = 123; x.b = 456; printf("in f(): sizeof(x)=%d,(&x)=0x%08xn",sizeof(x),&x); } // in main3.c #include<stdio.h> void f(void); int x; int y; int main() { f(); printf("in main(): sizeof(x)=%d,(&x)=0x%08x,x=%d,y=%d n",&x,&y,y); return 0; } 程序输出: in f(): sizeof(x)=8,(&x)=0x02489018 in main(): sizeof(x)=4,(&x)=0x02489018,(&y)=0x02489020,x=123,y=0 始终记住,外部符号决议的是地址,因此无论lib3.c和main3.c中,符号x地址都是唯一的,无论其被定义了几次。其次sizeof是编译器决议,与链接无关,编译器只看得到本模块的定义或声明。最后,由于符号x决议到lib3.c中的x,其size是8,因此main3.c中的y的地址比x大8,这是由链接器将lib3.o和main3.o合并后填入可执行文件的.data段的。因此y是无关变量,被初始化为0,注意和例2的区别。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |