传统的方法
网上的文章大多会让你使用 gcc 的 -fvisibility=hidden 来控制符号的可见性。将所有其它符号设为不可见,然后在源码中将需要导出的符号用__attribute__ ((visibility (“default”)))修饰,就可以控制要导出的符号了,参见hello.c的源码
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
int call_count = 0;
DLL_PUBLIC int hello_init()
{
call_count = 0;
return 0;
}
int hello_call_count_add()
{
return ++call_count;
}
DLL_PUBLIC int hello_handle()
{
return hello_call_count_add();
}
DLL_PUBLIC void hello_exit()
{
call_count = 0;
}
执行下列命令,显示出so文件的导出符号:
gcc -shared -fvisibility=hidden -o libhello.so hello.c
readelf --dyn-syms libhello.so
显示的导出符号如下:
Symbol table '.dynsym' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
4: 0000000000000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.2.5 (2)
5: 00000000000010e9 21 FUNC GLOBAL DEFAULT 10 hello_init
6: 0000000000001119 16 FUNC GLOBAL DEFAULT 10 hello_handle
7: 0000000000001129 17 FUNC GLOBAL DEFAULT 10 hello_exit
可以看到,没有修饰的 hello_call_count_add
函数没有被导出,而修饰了的hello_init函数被导出。
使用version script控制导出符号
使用-fvisibility
选项和__attribute__
来控制导出符号需要大量修改源代码,有没有更简单的方法呢?有,那就是version script。
首先是没有添加任何__attribute__
的hello.c源码:
int call_count = 0;
int hello_init()
{
call_count = 0;
return 0;
}
int hello_call_count_add()
{
return ++call_count;
}
int hello_handle()
{
return hello_call_count_add();
}
void hello_exit()
{
call_count = 0;
}
建立一个简单的version-script.txt文件,内容如下:
{
global:
hello_init;
hello_handle;
local:
*;
};
上述代码表示的意思是:
- 导出hello_init,hello_handle两个符号
- 其他符号都不导出
然后,我们在编译时,加上一个参数 -Wl,–version-script=version-script.txt,这样就可以使用 version script 来控制符号的导出了。
执行以下命令:
gcc -shared -o libhello.so hello.c -Wl,--version-script=version-script.txt
readelf --dyn-syms libhello.so
可以看到,只有hello_init,hello_handle两个符号被导出了,其余符号都没有导出
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
4: 0000000000000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.2.5 (2)
5: 00000000000010e9 21 FUNC GLOBAL DEFAULT 10 hello_init
6: 0000000000001119 16 FUNC GLOBAL DEFAULT 10 hello_handle