逛论坛时 有人说
现在linux系统里的.so大部分不是用gcc -shared生成的,本身不包含符号表,而符号表是保存在一个.a文件里的,链接时只需要那个.a就可以了,类似Windows那样。
使用方法1:
查看共享库的依赖库(NEEDED)和搜索名(SONAME)。
readelf -d <file_name>
例如:
#readelf -d libuClibc-0.9.30rc2.so
第一次听说这样的事,我也试试:
$nm /usr/lib/libm.so
nm: /usr/lib/libm.so: no symbols
果然如此,上网查了查,有人说是因为/usr/lib/libm.so是符号链接,找到真正的文件一试还是这样,其余符号链接和真实文件读取上没有什么差别。
又找了找,发现了一个不小的问题。
$ readelf -s /usr/lib/libm.so
...
35: 000092c0 80 FUNC WEAK DEFAULT 12 sin@@GLIBC_2.0
36: 00010530 125 FUNC WEAK DEFAULT 12 acosf@@GLIBC_2.0
37: 000094d0 124 FUNC WEAK DEFAULT 12 acosh@@GLIBC_2.0
38: 00013a00 15 FUNC WEAK DEFAULT 12 fmaf@@GLIBC_2.1
39: 00017c10 125 FUNC WEAK DEFAULT 12 acosl@@GLIBC_2.0
40: 0000b570 346 FUNC WEAK DEFAULT 12 catan@@GLIBC_2.1
...
使用readelf读取符号表完全正常,这又是怎么回事?
其余问题已经很明显了,nm输出的符号信息和.so的符号表完全是两回事。我们可以做个实验:
#include <iostream>
using namespace std;
void fun()
{
cout << "fun" << endl;
}
int main()
{
fun();
return 0;
}
这是一个很简单的c++程序(c++.cpp)。
$ g++ c++.cpp && ll a.out
-rwxr-xr-x 1 osily osily 6085 Dec 7 18:47 a.out*
分别用nm和readelf分析(篇幅所限就不列出来了):
osily@ly50247:/tmp
$ nm a.out|wc -l
45
osily@ly50247:/tmp
$ nm a.out|grep fun
080486b4 t _GLOBAL__I__Z3funv
08048634 T _Z3funv
osily@ly50247:/tmp
$ readelf -s a.out|wc -l
96
osily@ly50247:/tmp
$ readelf -s a.out|grep fun
46: 080486b4 28 FUNC LOCAL DEFAULT 13 _GLOBAL__I__Z3funv
65: 08048634 44 FUNC GLOBAL DEFAULT 13 _Z3funv
可以看出二者都有许多符号信息,也都有fun函数。
现在我找a.out strip一下:
osily@ly50247:/tmp
$ strip a.out
osily@ly50247:/tmp
$ nm -a a.out
nm: a.out: no symbols
osily@ly50247:/tmp
$ readelf -s a.out|wc -l
15
osily@ly50247:/tmp
$ readelf -s a.out
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@GLIBC_2.1.3 (2)
2: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
4: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (3)
5: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (4)
6: 00000000 0 FUNC GLOBAL DEFAULT UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (3)
7: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (3)
8: 0804878c 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
9: 08048568 0 FUNC GLOBAL DEFAULT UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (3)
10: 08048538 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (3)
11: 080499a0 140 OBJECT GLOBAL DEFAULT 25 _ZSt4cout@GLIBCXX_3.4 (3)
现在nm表示压力很大,已经无符号信息可输出。而readelf仍可以输出一些,但其中并没有fun函数。
再看下这个:
osily@ly50247:/tmp
$ g++ -c c++.cpp
osily@ly50247:/tmp
$ strip c++.o
osily@ly50247:/tmp
$ g++ c++.o
/usr/bin/ld: error in c++.o(.eh_frame); no .eh_frame_hdr table will be created.
/usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status
这说明nm输出的许多符号信息是用于链接的,而不是运行的。还有如果用g++ -g编译的话用nm -a还会读到调试信息。
再看这个:
osily@ly50247:/tmp
$ g++ -shared c++.cpp
osily@ly50247:/tmp
$ strip a.out
osily@ly50247:/tmp
$ readelf -s a.out
Symbol table '.dynsym' contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@GLIBC_2.1.3 (2)
2: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
4: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (3)
5: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (3)
6: 00000000 0 FUNC GLOBAL DEFAULT UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (3)
7: 00000000 0 OBJECT GLOBAL DEFAULT UND _ZSt4cout@GLIBCXX_3.4 (3)
8: 00000000 0 FUNC GLOBAL DEFAULT UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (3)
9: 00000000 0 FUNC GLOBAL DEFAULT UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (3)
10: 00000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.1.3 (2)
11: 00001950 0 NOTYPE GLOBAL DEFAULT ABS _end
12: 00001944 0 NOTYPE GLOBAL DEFAULT ABS _edata
13: 0000065c 44 FUNC GLOBAL DEFAULT 11 _Z3funv
14: 00001944 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
15: 00000688 20 FUNC GLOBAL DEFAULT 11 main
16: 0000053c 0 FUNC GLOBAL DEFAULT 9 _init
17: 00000738 0 FUNC GLOBAL DEFAULT 12 _fini
把c++.cpp编译链接成.so,strip,readelf仍然能发现fun函数。
所以一个.so的导出函数表不是用nm看的,而系统里的.so只是strip一下,可平时自己弄大没有太大的区别。