为什么 gdb 寻找汇编文件?

为什么 gdb 寻找汇编文件?

我正在尝试使用 Ubuntu 13.10 了解 gdb。我正在使用 gcc 编译器和以下示例:

https://en.wikipedia.org/wiki/Gdb#An_example_session

我得到的不是示例中所示的输出,而是:

Program received signal SIGSEGV, Segmentation fault.
__strlen_sse2_pminub () at ../sysdeps/x86_64/multiarch/strlen-sse2-pminub.S:38
38      ../sysdeps/x86_64/multiarch/strlen-sse2-pminub.S: No such file or directory.

为什么调试器要寻找我所认为的汇编源文件,而我显然没有安装它?

答案1

为什么它要寻找汇编源文件?

因为有问题的代码是用汇编语言编写的库代码,并且它的调试信息引用了该文件。(特别是,它是 [e]glibc针对(又名)的优化strlen()实现之一。)x86-64amd64

推测是被调试程序向 传递了一个错误的参数strlen(),导致其崩溃。(理论上,这可能是 的实现存在问题strlen(),但这样一个基本函数似乎不太可能出现错误,尤其是考虑到 的流行程度以及 [e]glibc在该架构上amd64只有 3 个选择这一事实strlen()

为什么该文件没有安装?

主要是因为安排这样的事情并不容易。

接下来是对所涉及问题的相当技术性的总结,并且可能包含很多你实际上并不具备的知识;如果你有任何问题,你可以在 #debian-mentors 上提问光缆连接技术委员会

重要事实:

  • Ubuntu 使用与 Debian 大致相同的源包集合,其中有相对较少的软件包不是直接来自 Debian。

  • Ubuntu 的大部分调试信息都包含在-dbgsym自动生成的包中dh_strip时间pkg-create-dbgsym(在 周围安装一个包装器dh_strip)。(这在 Debian 上不会发生。)这是有效的,因为 99% 的软件包都会用来dh_strip剥离它们的二进制文件。

  • 一些关键源包明确生成了自己的-dbg包,主要是为了在 Debian 上使用,但也可能因为它们需要安装的不仅仅是单独的 DWARF 信息。

  • eglibc是这些关键源包之一。

由于 Debian 源包在构建方式上拥有几乎无限的自由,因此对于通用工具来说,识别哪些文件正是应该捆绑到调试源包中的相关源文件绝非易事。

您可能认为只需解压源包就足够了,但对于gcc实际上在所谓的.origtarball 中包含 tarball 的包,或者甚至对于仅使用比您获得的格式更复杂的补丁系统的包,这种方法效果不佳。(另一个会搞砸事情的事情是,如果上游包在构建过程中生成任何缺少指令或在这些指令中引用自身的3.0 (quilt)源。)#line#line

然而,似乎二进制文件包含所有源文件的绝对路径,因此如果它们此时仍然完好无损可以打包。不幸的是,我似乎幻想到了objcopy允许你重写 DWARF 中的源路径以匹配安装路径的标志,所以 GDB仍然找不到它们

答案2

请注意,wiki 页面显示您的输出可能会有所不同。您可以按照关于这个例子的讨论这导致该警告被添加到 LinuxQuestions 的 Wiki 页面。

一般来说,这些差异取决于像libc(可能还有gdb)这样的内容在不同系统上的编译方式。请注意,示例是在 Fedora 上运行的,而不是 Ubuntu;因此基础系统在设置方式上可能存在一些关键差异。

我认为这是造成差异的主要原因。事实上,我在 Fedora 上运行了该示例,得到的输出与示例中的输出大致相同。

更重要的一点是,您收到的输出是 Ubuntu 的正常输出。我得到了相同的输出。您可以bt从此时发出命令,下一组输出将与示例中的输出非常相似。

那么,为什么在使用 C 语言时,gdb 会查找汇编文件呢?请记住,它libc正在查找汇编文件。GDB 只是报告发生了什么。

并且libc正在寻找汇编文件,本质上是因为它是应该的:库的各个部分都是用汇编实现的。

我并不是 C 标准库或汇编内部工作原理的专家,所以我要小心,不要说出不正确的话。

但是请注意这个例子的两点:

  1. 这是关于长度的问题,根据定义,它将进入系统相关的定义。

    如果你查看该消息,你会看到 lib 确实正在查看它自己的内部sysdeps目录,其中存储了系统相关目录。此目录是编译库的一部分,而不是计算机上可见文件系统的一部分。

    根据GNU C 库文档该结构是根据configure实用程序的结果创建的,它决定了库的编译方式。

    如果你看看源代码对于 libc,您将看到它strlen根据系统架构调用隐藏的内置函数,而后者又调用另一个隐藏的内置系统相关程序集(我数了一下,有 18 个文件同名,全部用于不同的架构),后者又调用(或可能调用?)用于不同处理器的strlen.S附加隐藏内置函数。和与处理器细节有关。.Sssepminub

  2. 为什么找不到该文件?我想是因为它不在那里。编译后的二进制文件不包含它(我猜)是因为这个系统不需要它,而且configure没有说在构建过程中应该创建它。

    这里的重点是,示例将 a 传递NULL给需要字符串的函数,这是一个大错误。与高级语言不同,编译器不会捕获此错误。结果是未定义的行为,因此任何事情都可能发生。

    我的猜测是,该库正在尝试找到它认为最有意义的函数版本;但由于NULL这里毫无意义,所以它猜错了并寻找不存在的版本。

    不管怎样,它都会失败并出现核心转储。我还猜测,为 Fedora 编译的库不会表现出完全相同的行为,因为存在各种系统差异,而且此时任何行为都是合法的。

答案3

我找到了答案本网站

以下是简短版本

  1. 从终端运行strace -o gdboutput.txt gdb example并浏览你链接到的 wiki 示例

  2. 打开gdboutput.txt并查找文本No such file or directory。在那里你会找到一条路径strlen-sse2-pminub.S

  3. 使用以下方式下载最新的 glibc 库sudo apt-get source glibc

  4. strlen-sse2-pminub.S在刚刚下载的目录中查找find -iname strlen-sse2-pminub.S

  5. 将 glibc 移动到步骤 2 中找到的位置。您可能需要创建目录。或者您可以更改 gdb 查找这些文件的位置(我还不太了解该怎么做)

现在运行 gdb 示例不会返回文件未找到错误。但它仍然没有给我维基百科的结果。我使用的是 Ubuntu。它在 cent-os 上运行良好,如维基百科所示。

如果网站关闭,我将复制下面的完整讨论。


在 Ubuntu 12.10 中调试 – 缺少文件 syscall-template.S 注意:本文底部有更新。请阅读!

将我的开发机器升级到 Ubuntu 12.04 后,我很失望地发现调试突然无法正常工作。每次我尝试时都会收到错误:

Cannot find file ‘../sysdeps/unix/syscall-template.S’

此后,所有的调试都变得没有多大意义(而重复的警告只是起到阻碍作用)。

生活有点不顺,我有一段时间没有做任何 C 开发工作——以至于 12.10 升级版就在我还没来得及考虑之前就来了。​​遗憾的是,问题并没有消失,所以在又一段时间不活动之后,我现在正在修复它。

我发现有问题的文件位于 libc6 的源中。可以使用以下命令获取这些文件:

sudo apt-get source libc6

这会将源代码下载到当前目录。返回调试器,它仍然找不到文件——这并不奇怪,但我不知道把它们放在哪里。绝望之下,我在调试器(在本例中为 nemiver)上运行了 strace,看看它在哪里寻找我们的朋友。这是我得到的:

stat(“/build/buildd/eglibc-2.15/misc/../sysdeps/unix/syscall-template.S”, 0x7fff0ba361f0) = -1 ENOENT (No such file or directory)
stat(“/home/test/../sysdeps/unix/syscall-template.S”, 0x7fff0ba361f0) = -1 ENOENT (No such file or directory)
stat(“/build/buildd/eglibc-2.15/misc/syscall-template.S”, 0x7fff0ba36200) = -1 ENOENT (No such file or directory)
stat(“/home/test/syscall-template.S”, 0x7fff0ba36200) = -1 ENOENT (No such file or directory)

由于不了解调试器的内部工作原理,并且希望让所有用户都可以看到这些文件,因此我决定创建目录/build/buildd并将 libc6 源放入其中:

sudo mkdir -p /build/buildd
cd /build/buildd
sudo mv ~/eglic* .

成功了!Nemiver 找到了文件,从此高高兴兴了。由于 nemiver 是 gdb 的包装器,我检查并确认它也是好的,所以如果你是那些能理解 gdb* 的疯狂人士之一,那么这对你来说就很有效。

顺便说一句,我不知道这是否是做这种事情的“正确”方法,我强烈怀疑它不是。但它有效,所以我很高兴。如果有人知道我应该怎么做,我会洗耳恭听。

*在学习 C 之前,我唯一的调试经验是 Java 调试,这是你能拥有的最棒的调试体验。在那之后,gdb 就是一场可怕的噩梦。

2014 年 5 月 6 日更新:看来 libc6 源代码在较新的 Ubuntu 版本中作为可安装包提供。您应该可以这样做

sudo apt-get install eglibc-source

所有内容都会安装在正确的位置,并且对调试器可见。

这些信息是我从 Adventures in Code 中得知的。

相关内容