![为什么 GDB 需要可执行文件以及核心转储?](https://linux22.com/image/117026/%E4%B8%BA%E4%BB%80%E4%B9%88%20GDB%20%E9%9C%80%E8%A6%81%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6%E4%BB%A5%E5%8F%8A%E6%A0%B8%E5%BF%83%E8%BD%AC%E5%82%A8%EF%BC%9F.png)
我正在使用核心转储进行调试,请注意,gdb 需要您提供可执行文件以及核心转储。为什么是这样?如果核心转储包含进程使用的所有内存,那么核心转储中是否包含可执行文件?也许无法保证整个 exe 都会加载到内存中(尽管单个可执行文件通常不会那么大),或者核心转储毕竟不包含所有相关内存?是为了符号(也许它们没有正常加载到内存中)?
答案1
核心转储只是程序内存占用的转储,如果您知道所有内容在哪里,那么您就可以使用它。
您使用可执行文件是因为它解释了事物在内存中的位置(即核心文件)(就逻辑地址而言)。
如果您使用命令,objdump
它将转储有关您正在调查的可执行对象的元数据。以名为 a.out 的可执行对象为例。
objdump -h a.out
仅转储标题信息,您将看到名为例如的部分。。数据或者.bss或者。文本(还有更多)。这些通知内核加载程序可以在对象中的何处找到各个部分,以及应在进程地址空间中的何处加载该部分,以及对于某些部分(例如.data .text)应加载什么。 (.bss 部分不包含文件中的任何数据,但它指的是进程中为未初始化数据保留的内存量,它用零填充)。
可执行目标文件的布局符合标准 ELF。
objdump -x a.out
- 倾倒一切
如果可执行对象仍然包含其符号表(它尚未被剥离 -man strip
并且您用于-g
生成调试生成以gcc
假设 ac 源编译),那么您可以通过符号名称检查核心内容,例如,如果您有一个变量/缓冲区命名的输入线在源代码中,您可以使用该名称来gdb
查看其内容。即gdb
知道从程序初始化数据段开始的偏移量,其中输入线开始和该变量的长度。
进一步阅读第1条, 第二条,以及实质内容可执行和链接格式 (ELF) 规范。
在下面@mirabilos 评论后更新。
但是如果使用符号表,如下所示
$ gdb --batch -s a.out -c core -q -ex "x buf1"
生产
0x601060 <buf1>: 0x72617453
然后不使用符号表并直接检查地址,
$ gdb --batch -c core -q -ex "x 0x601060"
生产
0x601060: 0x72617453
我直接检查了内存,而没有在第二个命令中使用符号表。
我还看到,@user580082 的答案进一步补充了解释,并将投票。
答案2
核心文件是进程终止时堆栈映像、内存映射和寄存器的快照。其内容可以按照给出的方式进行操作核心手册页。默认情况下,私有映射、共享映射和 ELF 头信息会转储到核心文件中。
回答你的问题,gdb之所以需要可执行文件,是因为它不像valgrind那样通过读取和解释二进制指令来模拟执行,而是成为进程的父进程,从而控制进程在运行时的行为。它使用核心文件来确定崩溃期间进程的内存映射和处理器状态。
在 Linux 中,父进程可以获得有关其子进程的附加信息,特别是 ptrace 的能力,这允许调试器访问进程的低级信息,例如读/写其内存、寄存器、更改信号映射、停止其执行等。
一旦您阅读了任何调试器的工作原理,您就会了解可执行文件的要求,尽管有更多的核心文件。
答案3
(除了其他好的答案)
在现代 Linux(和许多类 Unix)系统上,调试信息(包括有关符号类型、源代码位置、变量类型等的元数据......)位于矮人格式并位于极低频使用某些选项编译时可执行文件(或 ELF 共享库)-g
。我建议编译要调试的程序-g3 -O0
,也许-fno-inline
如果使用最近的海湾合作委员会;然而,使用 GCC,您甚至可以使用优化和调试信息进行编译,例如使用-O2 -g1
,尽管在这种情况下调试信息可能有点“模糊”(这可能稍微有助于捕获一些顽皮的信息)海森巴格)。
避免将该信息放入其中是非常明智的核core
文件,因为同一个可执行文件可能有许多不同的核心文件(想象一个广泛使用的软件,有许多用户制作错误报告,其中大多数带有转储)。还核心(5)文件由内核转储,内核不应该关心 DWARF 部分的存在小精灵(5)可执行文件(因为这些部分没有映射到虚拟地址空间断层的过程将核心倾倒在一些信号(7))。甚至可以将调试信息放入分离文件(可执行文件之外)。
顺便说一句,GDB 可以痛苦地用于调试可执行文件的核心转储,无需任何调试信息。但是,您实际上是在机器代码级别进行调试(而不是在编程语言及其编译器提供的符号级别)。
答案4
由于可执行文件在执行期间受到写保护,因此运行的进程无法更改它们。此外,当多个进程执行同一图像时,代码(和常量)页大部分必须加载到内存中一次。即使单个进程执行图像也不会将所有页面加载到 RAM 中(请求寻呼)。尽管如此,可执行文件的某些部分根本不需要加载到 RAM 中,例如调试信息。
出于所有这些原因,这是有道理的不是在每个核心转储文件中复制可执行文件;无论如何,这将是一个“共同因素”。
因此,要显示代码(例如disassemble
)gdb
需要代码页。理论上,如果核心转储中只包含请求分页代码,您可以列出程序的失败部分,而且调用代码页也可能已被“调出”,因此您可能只能检查程序的一部分失败。
还gdb
需要调试符号(如果有)将代码地址与源文件、行和变量名称关联起来。
最后,当使用动态库时,核心转储也必须包含所有库,否则。