16.15 制作so文件时如何指定导出哪些符号
http://scz.617.cn/unix/201507241022.txt
Q:
制作so文件只想导出几个函数,其余函数不导出,就像Windows下dll一样。
A: Ulrich Drepper [email protected] 2005-01-22
一般来说至少有四种办法:
- static
- attribute ((visibility("hidden")))
- Export Maps
- libtool: -export-symbols (实测无效)
更详细的介绍参看下文:
How To Write Shared Libraries http://people.redhat.com/drepper/dsohowto.pdf
A: scz 2015-07-24 10:22
虽然下面以可执行程序举例,但相关技术同样适用于共享库。
$ gcc --version gcc (Debian 4.9.2-10) 4.9.2
$ vi test_0.c
#include <stdio.h> #include <stdlib.h>
static unsigned int bar ( void ) { return( 100 ); } /* end of bar */
int main ( int argc, char * argv[] ) { unsigned int a, b;
a = bar();
b = ( unsigned int )rand();
return( a + b );
$ gcc -Wl,--export-dynamic -Wall -pipe -O0 -s -o test_0 test_0.c $ gcc -rdynamic -Wall -pipe -O0 -s -o test_0 test_0.c
"-Wl,--export-dynamic"与"-rdynamic"等效。
$ readelf -Ws test_0
Symbol table '.dynsym' contains 20 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable 2: 00000000 0 NOTYPE WEAK DEFAULT UND gmon_start 3: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2) 4: 00000000 0 FUNC GLOBAL DEFAULT UND rand@GLIBC_2.0 (2) 5: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 6: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable 7: 080498d8 0 NOTYPE GLOBAL DEFAULT 24 _edata 8: 080498d0 0 NOTYPE GLOBAL DEFAULT 24 __data_start 9: 080498dc 0 NOTYPE GLOBAL DEFAULT 25 _end 10: 080498d0 0 NOTYPE WEAK DEFAULT 24 data_start 11: 080486ac 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 12: 08048620 97 FUNC GLOBAL DEFAULT 13 __libc_csu_init 13: 080484e0 0 FUNC GLOBAL DEFAULT 13 _start 14: 080486a8 4 OBJECT GLOBAL DEFAULT 15 _fp_hw 15: 080498d8 0 NOTYPE GLOBAL DEFAULT 25 __bss_start 16: 080485e5 50 FUNC GLOBAL DEFAULT 13 main 17: 08048474 0 FUNC GLOBAL DEFAULT 11 _init 18: 08048690 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini 19: 08048694 0 FUNC GLOBAL DEFAULT 14 _fini $ nm -D test_0 080486ac R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable w _Jv_RegisterClasses 080498d8 B __bss_start 080498d0 D __data_start w gmon_start 08048690 T __libc_csu_fini 08048620 T __libc_csu_init U __libc_start_main 080498d8 D _edata 080498dc B _end 08048694 T _fini 080486a8 R _fp_hw 08048474 T _init 080484e0 T _start 080498d0 W data_start 080485e5 T main U rand
符号bar未被导出,因为用了static修饰符。
$ vi test_1.c
#include <stdio.h> #include <stdlib.h>
#define EXPORT attribute ((visibility("default")))
EXPORT unsigned int bar ( void ) { return( 100 ); } /* end of bar */
int main ( int argc, char * argv[] ) { unsigned int a, b;
a = bar();
b = ( unsigned int )rand();
return( a + b );
$ gcc -rdynamic -fvisibility=hidden -Wall -pipe -O0 -s -o test_1 test_1.c
"-fvisibility=hidden"将缺省visibility由default改为hidden,test_1.c中没有明 确EXPORT的符号便不会导出。
$ readelf -Ws test_1
Symbol table '.dynsym' contains 20 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTable 2: 00000000 0 NOTYPE WEAK DEFAULT UND gmon_start 3: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2) 4: 00000000 0 FUNC GLOBAL DEFAULT UND rand@GLIBC_2.0 (2) 5: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 6: 00000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable 7: 080498d8 0 NOTYPE GLOBAL DEFAULT 24 _edata 8: 080485db 10 FUNC GLOBAL DEFAULT 13 bar 9: 080498d0 0 NOTYPE GLOBAL DEFAULT 24 __data_start 10: 080498dc 0 NOTYPE GLOBAL DEFAULT 25 _end 11: 080498d0 0 NOTYPE WEAK DEFAULT 24 data_start 12: 080486ac 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 13: 08048620 97 FUNC GLOBAL DEFAULT 13 __libc_csu_init 14: 080484e0 0 FUNC GLOBAL DEFAULT 13 _start 15: 080486a8 4 OBJECT GLOBAL DEFAULT 15 _fp_hw 16: 080498d8 0 NOTYPE GLOBAL DEFAULT 25 __bss_start 17: 08048478 0 FUNC GLOBAL DEFAULT 11 _init 18: 08048690 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini 19: 08048694 0 FUNC GLOBAL DEFAULT 14 _fini $ nm -D test_1 080486ac R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable w _Jv_RegisterClasses 080498d8 B __bss_start 080498d0 D __data_start w gmon_start 08048690 T __libc_csu_fini 08048620 T __libc_csu_init U __libc_start_main 080498d8 D _edata 080498dc B _end 08048694 T _fini 080486a8 R _fp_hw 08048478 T _init 080484e0 T _start 080485db T bar 080498d0 W data_start U rand
与Export Maps技术相比,visibility技术生成的代码更高效,hidden的函数不经过 PLT。
$ vi test_2.c
#include <stdio.h> #include <stdlib.h>
unsigned int bar ( void ) { return( 100 ); } /* end of bar */
int main ( int argc, char * argv[] ) { unsigned int a, b;
a = bar();
b = ( unsigned int )rand();
return( a + b );
$ vi test_2.map
{ global :
bar;
_IO_stdin_used;
local :
*;
$ gcc -Wl,--version-script=test_2.map -rdynamic -Wall -pipe -O0 -s -o test_2 test_2.c
Export Maps必须与-rdynamic一起使用,否则无意义。
test_2.map中额外指定了_IO_stdin_used,否则该符号不会被导出,而test_1导出了 该符号。这是个啥,有必要导出吗?
$ readelf -Ws test_2
Symbol table '.dynsym' contains 6 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 NOTYPE WEAK DEFAULT UND gmon_start 2: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2) 3: 00000000 0 FUNC GLOBAL DEFAULT UND rand@GLIBC_2.0 (2) 4: 0804840b 10 FUNC GLOBAL DEFAULT 13 bar 5: 080484dc 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used $ nm -D test_2 080484dc R _IO_stdin_used w gmon_start U __libc_start_main 0804840b T bar U rand
libtool的"-export-symbols"是传说中的第4种方案,据我实测无效。
$ vi test_2.sym
$ libtool --mode=compile gcc -Wall -pipe -O0 -c -o test_3.o test_2.c $ libtool --mode=link gcc -export-symbols test_2.sym -rdynamic -Wall -pipe -O0 -s -o test_3 test_3.o
或
$ libtool --mode=link gcc -export-symbols-regex "^bar" -rdynamic -Wall -pipe -O0 -s -o test_4 test_2.c
"-export-symbols"或"-export-symbols-regex"直接被忽略了。放狗搜了一下,好像 在Linux上就是无效。