GDB 显示调用堆栈上函数地址的当前编译二进制文件
在 Visual Studio 中,您可以看到函数所在的位置,即在哪个编译的二进制文件中.例如:
In visual studio you can see where the function is located, i.e. within which compiled binary file. For example:
GDB 有命令吗?
如果你加载一个 dll,一个符号的多个版本可以加载两次.必须了解代码是在 .exe 中执行还是在已加载的 .dll 库之一中执行.Visual Studio 调试器在提供的屏幕截图中显示此信息(虽然它是针对 C#,但我只是从网站上复制了它).
Several versions of one symbol can be loaded twice in case you load a dll. And it is essential to know whether the code is being executed within the .exe or one of loaded .dll libs. Visual Studio debugger shows this info like on the screenshot provided (though it is for C#, I just copied it from the web site).
当我使用 Visual Studio 时,我能够发现一些情况,即预期在 .dll 中执行的代码在 .exe 中执行.当然,这是由于链接命令错误造成的.但是这种错误可能很难发现,查看当前执行的二进制文件会有很大帮助.
When I used Visual Studio, I was able to spot some cases, when the code expected to be executed within a .dll was executed within .exe. Of course this was due to a mistake in linking command. But this kind of mistakes may be hard to spot, and seeing the current binary file of execution helps a lot.
推荐答案
免责声明:这是针对 GNU/Linux 上的 GDB,使用 ELF 文件.GDB 手册并没有说我在这里展示的命令是特定于 Linux 的,但我不知道它们是否会在 Windows 上产生类似的结果.
Disclaimer: this is for GDB on GNU/Linux, working with ELF files. The GDB manual doesn't say that the commands I show here are Linux-specific, but I don't know whether they'll produce similar results on Windows.
GDB 的 信息符号 命令,给定一个地址,将输出与该地址对应的目标文件中最接近的符号(和偏移量)、文件名和部分.
GDB's info symbol command, given an address, will output the closest symbol (and offset), filename and section of the object file corresponding to that address.
这是一个示例,主程序从两个不同的共享库访问 foo
函数.foo
只调用 lseek
,这将是放置断点的方便位置,因为它不会在程序的其他任何地方使用,包括 dl 函数.
Here's an example where the main program accesses the foo
function from two different shared libraries. foo
just calls lseek
, which will be a convenient spot to place a breakpoint because it isn't used anywhere else in the program, including the dl functions.
$ 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
命令将显示可执行文件或目标文件的名称.
Here's the GDB session. The bt
command will show the PC address of each frame, as well as the name of the source file, since everything was compiled with GCC's -g
option. The info sym
command will show the name of the executable or object file.
$ 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
相关文章