免责声明:这适用于 GNU/Linux 上的 GDB,用于处理 ELF 文件。 GDB 手册并没有说我在这里展示的命令是特定于 Linux 的,但我不知道它们是否会在 Windows 上产生类似的结果。
GDB 的info symbol 命令,给定一个地址,将输出与该地址对应的最接近的符号(和偏移量)、文件名和目标文件的部分。
这是一个示例,其中主程序从两个不同的共享库访问foo 函数。 foo 只调用了lseek,这将是放置断点的方便位置,因为它不会在程序的其他任何地方使用,包括 dl 函数。
$ nl -ba a.c
1 #include <sys/types.h>
2 #include <unistd.h>
3 void foo() {
4 lseek(0, 0, SEEK_CUR);
5 }
$ cc -fpic -shared -g a.c -o a.so
$ nl -ba b.c
1 #include <sys/types.h>
2 #include <unistd.h>
3 void foo() {
4 lseek(0, 0, SEEK_CUR);
5 }
$ cc -fpic -shared -g b.c -o b.so
$ nl -ba main.c
1 #include <dlfcn.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 int main() {
5 void *a = dlopen("./a.so", RTLD_LAZY|RTLD_LOCAL);
6 void *b = dlopen("./b.so", RTLD_LAZY|RTLD_LOCAL);
7
8 void (*afoo)() = (void (*)()) dlsym(a, "foo");
9 void (*bfoo)() = (void (*)()) dlsym(b, "foo");
10
11 (*afoo)();
12 (*bfoo)();
13 }
$ cc main.c -g -ldl -o main
这是 GDB 会话。 bt 命令将显示每一帧的 PC 地址,以及源文件的名称,因为所有内容都是使用 GCC 的 -g 选项编译的。 info sym 命令将显示可执行文件或目标文件的名称。
$ gdb -q ./main
(gdb) start
Temporary breakpoint 1, main () at main.c:5
5 void *a = dlopen("./a.so", RTLD_LAZY|RTLD_LOCAL);
(gdb) b lseek
Breakpoint 2 at 0x7ffffef38d30: file ../sysdeps/unix/syscall-template.S, line 84.
(gdb) c
Continuing.
Breakpoint 2, lseek64 () at ../sysdeps/unix/syscall-template.S:84
84 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) bt
#0 lseek64 () at ../sysdeps/unix/syscall-template.S:84
#1 0x00007ffffec40688 in foo () at a.c:4
#2 0x000000000800078b in main () at main.c:11
(gdb) info sym 0x00007ffffec40688
foo + 24 in section .text of ./a.so
(gdb) c
Continuing.
Breakpoint 2, lseek64 () at ../sysdeps/unix/syscall-template.S:84
84 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) bt
#0 lseek64 () at ../sysdeps/unix/syscall-template.S:84
#1 0x00007ffffea30688 in foo () at b.c:4
#2 0x0000000008000796 in main () at main.c:12
(gdb) info sym 0x00007ffffea30688
foo + 24 in section .text of ./b.so