程序的大致框架:
主程序:
#include <stdio.h>
extern void hello(void);
extern int a;
int main(void)
{
printf("in main.c a=%d\n", a);
printf("in main.c &a=%p\n", &a);
hello();
return 0;
}
共享对象:
#include <stdio.h>
int a = 1;
void hello(void)
{
printf("lib.c a=%d\n", a);
printf("lib.c &a=%p\n", &a);
return;
}
考虑这么几种情况
(1)共享对象定义了静态变量a=1,主程序定义了全局变量a并且没有初始化,分别在共享对象和主程序里面打印a的值
结果:共享对象中a=1,主程序a=0.
分析:因为共享对象中使用了静态变量,属于模块内部的数据引用,使用了位置无关代码,并且a不需要重定向,因此a=1,主程序中由于a没有定义,被分配到了.bss段,当加载程序时初始化为0.
(2)共享对象定义了全局变量a=1,主程序定义了全局变量a并且没有初始化,分别在共享对象和主程序里面打印a的值
结果:共享对象中a=1,主程序a=1.
分析:因为共享对象中使用了全局变量,属于模块之间的数据引用,此时a被放在了.got段,需要重定向,主程序中由于a定义了,被分配到了.bss段,当加载程序时,由于主程序中a没有初始化,在动态链接时,会把共享对象中a的值拷贝到主程序中a的地址,于是a=1,因此无论是在主程序还是在共享对象中,两者打印的都是主程序中a的值。通过打印a的地址也可以知道,打印的是同一个地址的值。
这时我们查看一下a在两个模块中动态链接的信息:
主程序动态链接信息:
Relocation section '.rela.dyn' at offset 0x4d8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000600a60 000700000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000600ab0 000900000005 R_X86_64_COPY 0000000000600ab0 a + 0
共享对象动态链接信息:
Relocation section '.rela.dyn' at offset 0x400 contains 9 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000200758 000000000008 R_X86_64_RELATIVE 0000000000000640
000000200760 000000000008 R_X86_64_RELATIVE 0000000000000600
000000200988 000000000008 R_X86_64_RELATIVE 0000000000200988
000000200930 000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200938 000700000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200940 000900000006 R_X86_64_GLOB_DAT 0000000000200990 a + 0
000000200948 000b00000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
很明显在主程序中,从Type属性看,a其实是一个copy,复制了共享对象中的a的值。而共享对象,a的值是一个需要重定向的数据,存放在.got.
(3)共享对象定义了全局变量a=1,主程序定义了全局变量a=2,分别在共享对象和主程序里面打印a的值
结果:共享对象中a=2,主程序a=2.
分析:这个和情况2有点类似,只是主程序中a已经有值了,动态链接时,不会把共享对象中a的值复制到主程序中,剩下的重定向一样。
这个例子很好的说明了共享对象和主程序中数据的重定向,当然这个例子也可以延伸为函数的调用,不过基本思想大致一样。