Linux查看共享库导出的函数列表
Contents
1. 概述
在本教程中,我们将了解 Linux 共享库中的导出符号以及如何查看它们。
2. 共享库中的导出符号
外部程序只能使用从共享库中导出的符号。
让我们用一个例子来证明这一点。首先,让我们创建一个名为lib.so的共享库并从中导出符号:
$ cat lib.c
#include <stdio.h>
void lib_exported1(void) {
printf("Hello, this is an exported symbol\n");
}
void lib_exported2(void) {
printf("Hello, this is another exported symbol\n");
}
static void lib_private(void) {
printf("This function is static and can't be used from outside\n");
}
$ gcc lib.c -shared -o lib.so
我们使用带有*-shared标志的gcc 来输出一个共享库。**在这里,函数lib_private被标记为static函数并且不会被导出,因为static*函数无法在它们所在的文件之外访问。**
现在,让我们尝试链接到私有符号:
$ cat program.c
// Forward declarations
void lib_exported1(void);
void lib_exported2(void);
void lib_private(void);
int main(void) {
lib_exported1();
lib_exported2();
lib_private();
}
$ cc program.c lib.so
/usr/bin/ld: /tmp/ccfZyf8j.o: in function `main':
program.c:(.text+0xf): undefined reference to `lib_private'
collect2: error: ld returned 1 exit status
如我们所见,我们无法链接到未导出的私有符号。
此外,如果库是用 C++ 编写的,符号名称可能会被破坏。这意味着像lib_exported1这样的符号名称可能显示为_Z13lib_exported1v。大多数实用程序可以使用特殊标志处理这些符号。**
3. 列出共享库的导出符号
现在,让我们学习如何借助上面示例中创建的库来查看库的导出符号。
3.1. 使用readelf
我们可以使用带有*-s*标志的readelf 命令来查看导出的符号:
$ readelf -s lib.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 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
5: 0000000000000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.2.5 (2)
6: 000000000000111f 22 FUNC GLOBAL DEFAULT 12 lib_exported2
7: 0000000000001109 22 FUNC GLOBAL DEFAULT 12 lib_exported1
...
在这里,我们可以看到lib_exported1和lib_exported2函数,但看不到私有lib_private函数。其他符号如puts属于 C 库,即glibc 。readelf不支持符号名称的反修饰。
3.2. 使用objdump
我们还可以使用带有*-T*标志的objdump 命令来查看导出的符号:
$ objdump -T lib.so
lib.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 w D *UND* 0000000000000000 Base _ITM_deregisterTMCloneTable
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 puts
0000000000000000 w D *UND* 0000000000000000 Base __gmon_start__
0000000000000000 w D *UND* 0000000000000000 Base _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
000000000000111f g DF .text 0000000000000016 Base lib_exported2
0000000000001109 g DF .text 0000000000000016 Base lib_exported1
让我们将我们的库编译为 C++,看看objdump如何处理损坏的符号:
$ g++ lib.c -shared -o lib.so
$ objdump -T lib.so
lib.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
...
0000000000001109 g DF .text 0000000000000016 Base _Z13lib_exported1v
000000000000111f g DF .text 0000000000000016 Base _Z13lib_exported2v
默认情况下它不会分解符号,所以我们必须传递–demangle*标志:*
$ objdump -T --demangle lib.so
lib.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
...
0000000000001109 g DF .text 0000000000000016 Base lib_exported1()
000000000000111f g DF .text 0000000000000016 Base lib_exported2()
3.3. 使用nm
最后,我们还可以使用带有-D标志的nm 命令来查看导出的符号。它可以像objdump一样使用*–demangle*标志来分解名称:**
$ nm -D --demangle lib.so
w __cxa_finalize@GLIBC_2.2.5
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U puts@GLIBC_2.2.5
0000000000001109 T lib_exported1()
000000000000111f T lib_exported2()