【问题标题】:GDB show current compiled binary file for function address on call stackGDB 显示调用堆栈上函数地址的当前编译二进制文件
【发布时间】:2021-05-26 06:24:18
【问题描述】:

在 Visual Studio 中,您可以看到函数所在的位置,即在哪个编译的二进制文件中。例如:

GDB 有命令吗?


如果你加载一个 dll,一个符号的多个版本可以加载两次。 必须了解代码是在 .exe 中执行还是在已加载的 .dll 库之一中执行。 Visual Studio 调试器在提供的屏幕截图中显示此信息(虽然它是用于 C#,但我只是从网站上复制了它)。

当我使用 Visual Studio 时,我能够发现一些情况,即预期在 .dll 中执行的代码在 .exe 中执行。当然,这是由于链接命令错误造成的。但是这种错误可能很难发现,查看当前执行的二进制文件会有很大帮助。

【问题讨论】:

  • 查看documentation,查找btwhere
  • @AlanBirtles for backtrace 有一些示例显示源文件,如 builtin.c:993 。我正在寻找backtrace 的选项,但找不到显示二进制文件名所需的选项
  • 我不认为 gdb 可以按原样执行此操作,但这可能通过自定义 Frame Decorator 来实现。
  • @ssbssa 我会调查一下,谢谢

标签: c++ debugging gdb


【解决方案1】:

免责声明:这适用于 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

【讨论】:

  • 非常感谢!我会用mingw检查一下。
【解决方案2】:

存储在可执行文件中的调试信息提供了当前代码是从哪个源文件以及源代码的哪一行生成的信息。像目标文件这样的中间文件不是此类信息的一部分,也不总是可用的。如果您一步编译代码(编译和链接),您将在某处只有一些临时生成的文件。尤其是在这种情况下,引用对象文件毫无意义。

你从VS显示的信息也是源代码位置!

您还应该注意到,像 C 和 C++ 这样的编译语言与像 C# 这样的语言完全不同,即使还有某种“字节码生成”或其他什么。

也许我有一个误解,但是需要查看在编译和链接之间生成/放置当前代码的中间对象文件的名称是什么?二进制文件本身是完整链接的程序,因此二进制文件的名称将始终是您已启动且当前正在调试的可执行文件的名称。

如果您真的需要知道哪个目标文件包含您要查找的代码,您可以使用objdump -t 获取完整二进制文件以及所有对象的所有符号列表文件。在 C++ 的情况下,您应该将它与 demangling 结合使用,可能与 c++filt 一起使用。

【讨论】:

  • 我不想看到 intermediate 对象。我想查看当前的二进制文件。一个符号的两个版本可以加载两次,以防您链接到一个 dll。了解代码是在 .exe 中执行还是在 其中一个 加载的 .dll 库中执行是重要。 Visual Studio 调试器在提供的屏幕截图中显示此信息(虽然它是用于 C#,但我只是从网站上复制了它)。
猜你喜欢
  • 2012-04-21
  • 2012-08-14
  • 1970-01-01
  • 2017-02-24
  • 2014-06-17
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
相关资源
最近更新 更多