(1)动态库同名符号和visibility选项
结论:
1) 不加任何导出可见参数时,gcc编译动态库时默认导出所有的函数和全局变量(即可认为在全局变量和函数前都有__attribute__((__visibility__("default")))修饰),加了-fvisibility=hidden之后,则所有的函数和全局变量都不再导出(即可认为在全局变量和函数前都有__attribute__((__visibility__("hidden")))修饰),要导出特定的全局变量和函数需要手动显式地在前面添加__attribute__((__visibility__("default")))进行修饰。
2) 不同动态库中有同名的导出符号(全局变量或函数),并不影响主程序编译运行,但是容易导致混淆。此种情况,无论是通过dlopen方式动态加载,还是通过编译选项-l编译连接来引用动态库,主程序连接的始终是第一个被连接的符号地址,后续的符号全部被忽略,可参考:http://blog.csdn.net/zhongyunde/article/details/5939733
案例如下:
t1.c
#include <stdio.h>
int g_var = 11;
void f1()
{
printf("%d\n", g_var);
}
t2.c
#include <stdio.h>
int g_var = 22;
void f2()
{
printf("%d\n", g_var);
}
main.c
#include <stdio.h>
int main()
{
f1();
f2();
f1();
return 0;
}
编译连接
gcc -shared -fPIC -o libt1.so t1.c
gcc -shared -fPIC -o libt2.so t2.c
gcc -L./ -lt1 -lt2 -o main main.c
运行:
export LD_LIBRARY_PATH=./
./main
运行结果如下:
11
11
11
修改编译main时连接动态库的顺序:
gcc -L./ -lt2 -lt1 -o main main.c
再次运行:
./main
运行结果如下:
22
22
22
以上运行结果可以看出,main主程序始终连接首先加载动态库的导出全局符号g_var,另一个则被忽略。
采用dlopen试试看如何。
修改main.c为如下:
#include <stdio.h>
#include <dlfcn.h>
int main()
{
void (*f1)();
void (*f2)();
void* h1;
void* h2;
h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
f1 = dlsym(h1, "f1");
f2 = dlsym(h2, "f2");
f1();
f2();
f1();
dlclose(h1);
dlclose(h2);
return 0;
}
重新编译运行看看
gcc -L./ -ldl -lt1 -lt2 -o main main.c
./main
输入如下:
11
11
11
从结果看,和之前之一样的。
如果采用dlopen调用不同动态库的同名导出函数又如何。
修改t2.c如下:
#include <stdio.h>
int g_var = 22;
void f1()
{
printf("f1 in t2 %d\n", g_var);
}
修改main.c
#include <stdio.h>
#include <dlfcn.h>
int main()
{
void (*f1)();
void (*f2)();
void* h1;
void* h2;
h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
f1 = dlsym(h1, "f1");
f2 = dlsym(h2, "f1");
f1();
f2();
f1();
dlclose(h1);
dlclose(h2);
return 0;
}
编译运行:
gcc -shared -fPIC -o libt2.so t2.c
gcc -L./ -ldl -o main main.c
运行结果如下:
11
f1 in t2 11
11
从运行结果看,不同so动态库同名导出函数,用dlopen动态加载方式,由于地址不同,可以明显的区别,不会有任何冲突和混淆。
同名导出函数是否可以用-ldl选项进行编译,是否会冲突呢,调用的又是哪个库的函数呢?
修改main.c函数,如下:
#include <stdio.h>
int main()
{
f1();
return 0;
}
编译运行:
gcc -L./ -ldl -lt1 -lt2 -o main main.c
./main
输出
11
t2和t1连接顺序换一下,看一下输出如下:
gcc -L./ -ldl -lt2 -lt1 -o main main.c
./main
输出
f1 in t2 22
从运行结果看也是先连接的先运行,后续相同的函数被忽略。
解决方法,对于g_var可以通过visibility参数来禁止导出,这样每个so里面的全局变量,都在本so里面用,外面无法使用。
修改后的代码如下:
t1.c
#include <stdio.h>
int g_var = 11;
__attribute__((__visibility__("default"))) void f1()
{
printf("g_var in t1 = %d\n", g_var);
}
t2.c
#include <stdio.h>
int g_var = 22;
__attribute__((__visibility__("default"))) void f2()
{
printf("g_var in t2 = %d\n", g_var);
}
main.c
#include <stdio.h>
#include <dlfcn.h>
int main()
{
void (*f1)();
void (*f2)();
void* h1;
void* h2;
h1 = dlopen("./libt1.so", RTLD_LAZY | RTLD_GLOBAL);
h2 = dlopen("./libt2.so", RTLD_LAZY | RTLD_GLOBAL);
f1 = dlsym(h1, "f1");
f2 = dlsym(h2, "f2");
f1();
f2();
f1();
dlclose(h1);
dlclose(h2);
return 0;
}
增加-fvisibility=hidden选项重新编译运行,如下:
gcc -shared -fPIC -fvisibility=hidden -o libt1.so t1.c
gcc -shared -fPIC -fvisibility=hidden -o libt2.so t2.c
gcc -L./ -ldl -o main main.
运行结果如下:
g_var in t1 = 11
g_var in t2 = 22
g_var in t1 = 11
从结果看,不同so里面的全部变量g_var都在本so中有效,互补影响了,这种方式是推荐的方式,gcc自带的so库都是通过这种方式,默认全部不导出,要导出那些函数和变量,自行的显式地添加__attribute__((__visibility__("default")))来修饰。