之前讲过了静态库和动态库同名的问题,现在来更加深入的了解一下。
两个动态库调用含同名符号的静态库
先说一下为什么会出现这种情况?比如有两个不同的开源的动态库,但是这两个动态库都同时调用了一个静态库,比如openssl,但是这两个动态库调用的openssl的版本是不同的,可能存在一定的兼容性问题,这样,当一个工程中同时调用这两个开源库的时候可能会出现问题。我们来测试一下,首先是两个不同的静态库:
//lib_fun1.c
#include <stdio.h>
#include "lib_fun1.h"
int fun()
{
printf("in lib_fun1\n");
return 0;
}
//lib_fun2.c
#include <stdio.h>
#include "lib_fun2.h"
int fun()
{
printf("in lib_fun2\n");
return 0;
}
然后是两个动态库,这两个动态库分别调用上面的静态库:
//lib_so1.c
#include "lib_fun1.h"
int fun_so1()
{
fun();
return 0;
}
//lib_so2.c
#include "lib_fun1.h"
int fun_so2()
{
fun();
return 0;
}
然后同时调用这两个动态库:
//main.c
int fun_so1();
int fun_so2();
int main()
{
fun_so1();
fun_so2();
return 0;
}
#Makefile
all:main
lib_so1.so: lib_so1.o lib_a1.a
gcc -shared -o $@ $< -l_a1 -L.
lib_so2.so: lib_so2.o
gcc -shared -o $@ $< -l_a2 -L.
lib_a1.a: lib_fun1.o lib_a2.a
ar rcs $@ $<
lib_a2.a: lib_fun2.o
ar rcs $@ $<
main: main.o lib_so1.so lib_so2.so
gcc -o $@ main.o -l_so1 -l_so2 -L. -Wl,--rpath=.
%.o : %.c
gcc -fPIC -c $< -o $@
clean:
rm -rf *.o *.a *.so main
编译执行:
# make
gcc -fPIC -c main.c -o main.o
gcc -fPIC -c lib_so1.c -o lib_so1.o
gcc -fPIC -c lib_fun1.c -o lib_fun1.o
gcc -fPIC -c lib_fun2.c -o lib_fun2.o
ar rcs lib_a2.a lib_fun2.o
ar rcs lib_a1.a lib_fun1.o
gcc -shared -o lib_so1.so lib_so1.o -l_a1 -L.
gcc -fPIC -c lib_so2.c -o lib_so2.o
gcc -shared -o lib_so2.so lib_so2.o -l_a2 -L.
gcc -o main main.o -l_so1 -l_so2 -L. -Wl,--rpath=.
# ./main
in lib_fun1
in lib_fun1
我们发现两个库输出相同的信息,第二个库并不是调用自身的函数。我们交换一下链接的顺序,会发现输出改变了:
# gcc -o main main.o -l_so2 -l_so1 -L. -Wl,--rpath=.
# ./main
in lib_fun2
in lib_fun2
我们看一下fun符号的属性,以便后面做对比:
readelf -s lib_so1.so |grep fun
11: 000000000000066f 23 FUNC GLOBAL DEFAULT 12 fun
12: 000000000000065a 21 FUNC GLOBAL DEFAULT 12 fun_so1
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS lib_fun1.c
47: 000000000000066f 23 FUNC GLOBAL DEFAULT 12 fun
48: 000000000000065a 21 FUNC GLOBAL DEFAULT 12 fun_so1
我们发现fun符号的属性时GLOBAL,也就是全局可见的。如何解决这个问题?
怎么解决这个问题?
显然不能在函数前面加上static,因为他们是跨文件引用的。此时可以使用__attribute__((visibility(“hidden”))),这个修饰的意思是使符号对库外不可见。
我们修改一下代码:
//lib_fun1.c
#include <stdio.h>
#include "lib_fun1.h"
__attribute__((visibility("hidden"))) int fun()
{
printf("in lib_fun1\n");
return 0;
}
//lib_fun2.c
#include <stdio.h>
#include "lib_fun2.h"
__attribute__((visibility("hidden"))) int fun()
{
printf("in lib_fun2\n");
return 0;
}
编译执行:
# ./main
in lib_fun1
in lib_fun2
我们发现程序正常输出了。
我们再看一下符号的属性:
readelf -s lib_so1.so |grep fun
11: 000000000000060a 21 FUNC GLOBAL DEFAULT 12 fun_so1
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS lib_fun1.c
38: 000000000000061f 23 FUNC LOCAL DEFAULT 12 fun
48: 000000000000060a 21 FUNC GLOBAL DEFAULT 12 fun_so1
我们发现fun符号的属性不再是GLOBAL,而是LOCAL,也就是本地的,外部不能调用,类似static
注:
- attribute((visibility(“hidden”)))是声明,并不一定需要加在在函数定义的时候,也可以在头文件中声明
- 也可以在编译中添加参数**-fvisibility=hidden达到相同的效果,该参数加在编译**.c的时候,而不是链接
- 如果我们看一下生成的.o的话可能有一些区别:
readelf -s lib_so1.o |grep fun
8: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 fun_so1
9: 0000000000000000 0 NOTYPE GLOBAL HIDDEN UND fun
fun符号的属性是HIDDEN