有关于全局符号介入与多个动态库存在相同的符号相互覆盖的问题
小弟最近在看《程序员的自我修养》,书中提到了全局符号介入问题,根据书中的代码进行了实验,实验代码如下:
a1.c
#include <stdio.h>
void a()
{
printf("a1.c\n");
}
复制代码
a2.c
#include <stdio.h>
void a()
{
printf("a2.c\n");
}
复制代码
b1.c
void a();
void b1()
{
a();
}
复制代码
b2.c
void a();
void b2()
{
a();
}
复制代码
编译命令如下:
gcc -fPIC -shared a1.c -o a1.so
gcc -fPIC -shared a2.c -o a2.so
gcc -fPIC -shared b1.c a1.so -o b1.so
gcc -fPIC -shared b2.c a2.so -o b2.so
复制代码
最后是main.c
#include <stdio.h>
void b1();
void b2();
int main()
{
b1();
b2();
return 0;
}
复制代码
编译命令如下:
gcc main.c b1.so b2.so -o main -Xlinker -rpath ./
复制代码
最后程序运行结果如下:
./main
a1.c
a1.c
复制代码
根据书中给出的解释是:当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。到现在为止一切正常。
接下来问题就来了,偶然在网上看到了一段代码,并测试之。
foo.c
#include <stdio.h>
struct {
int a;
int b;
} b = { 3, 3 };
int main();
void foo()
{
b.a = 4;
b.b = 4;
printf("foo:\t(&b)=0x%08x\n\tsizeof(b)=%d\n\tb.a=%d\n\tb.b=%d\n\tmain:0x%08x\n",
&b, sizeof b, b.a, b.b, main);
}
复制代码
t1.c
#include <stdio.h>
int b = 1;
int c = 1;
int main()
{
int count = 5;
while (count-- > 0) {
t2();
foo();
printf("t1:\t(&b)=0x%08x\n\t(&c)=0x%08x\n\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
sleep(1);
}
return 0;
}
复制代码
t2.c
#include <stdio.h>
int b;
int c;
int t2()
{
printf("t2:\t(&b)=0x%08x\n\t(&c)=0x%08x\n\tsizeof(b)=%d\n\tb=%d\n\tc=%d\n",
&b, &c, sizeof b, b, c);
return 0;
}
复制代码
makefile如下:
export LD_LIBRARY_PATH:=.
test: t1.o t2.o
gcc -shared -fPIC -o libfoo.so foo.c
gcc -o test t1.o t2.o -L. -lfoo
t1.o: t1.c
t2.o: t2.c
.PHONY:clean
clean:
rm -f *.o *.so test*
复制代码
首先只对程序源码静态分析就可以发现:强符号b被定义了两次,不过编译时并没有报重定义错误,这是我的第一个问题?接下来看运行。
t2: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=1
c=1
foo: (&b)=0x00601050
sizeof(b)=8
b.a=4
b.b=4
main:0x00400766
t1: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
t2: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
foo: (&b)=0x00601050
sizeof(b)=8
b.a=4
b.b=4
main:0x00400766
t1: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
t2: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
foo: (&b)=0x00601050
sizeof(b)=8
b.a=4
b.b=4
main:0x00400766
t1: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
t2: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
foo: (&b)=0x00601050
sizeof(b)=8
b.a=4
b.b=4
main:0x00400766
t1: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
t2: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
foo: (&b)=0x00601050
sizeof(b)=8
b.a=4
b.b=4
main:0x00400766
t1: (&b)=0x00601050
(&c)=0x00601054
sizeof(b)=4
b=4
c=4
复制代码
通过程序可以清晰的发现,一开始b、c的值都还是1,但调用动态链接库中的foo函数后,b、c的内存地址没有改变,但值却改变了。
通过readelf -r 命令可以发现,test中并不存在b、c符号,证明上述符号在形成可执行文件时已完成重定位,但通过赋值情况来看,好像是运行加入的libfoo.so中的b将t1.c中的b覆盖了。
说了这么多,可能大家都乱了,现在把我的问题给大家总结以下
在第一个例子中:后加入的全局符号被忽略。
在第二个例子中:后加入的全局符号又覆盖前面被加入符号。
这两个例子从我直观上看是前后矛盾的,所以哪位大神可以给我分析,小弟万分感谢。