【问题标题】:Why are my core dumps missing an NT_FILE note?为什么我的核心转储缺少 NT_FILE 注释?
【发布时间】:2016-09-29 10:31:42
【问题描述】:

我在我的 Fedora 系统上设置了“ulimit -c unlimited”,所以段错误会生成核心转储文件。这是有效的。

我在这些 URL 中看到了 NT_FILE 注释:

ELF core file format

Anatomy of an ELF core file

但我的核心文件只包含这些注释:

$ readelf --notes core.simple.11

Notes at offset 0x000003f8 with length 0x00000558:
  Owner     Data size   Description
  CORE      0x00000150  NT_PRSTATUS (prstatus structure)
  CORE      0x00000088  NT_PRPSINFO (prpsinfo structure)
  CORE      0x00000130  NT_AUXV (auxiliary vector)
  CORE      0x00000200  NT_FPREGSET (floating point registers)

为什么没有 NT_FILE 注释? 我怎样才能找出核心文件可能基于的各种目标文件,更重要的是,这些文件映射到核心映像的虚拟地址?

如果没有来自 NT_FILE 注释的地址映射信息,我不知道如何在目标文件的 DWARF 调试信息中执行代码地址查找。

核心文件中的程序头:

$ readelf --segments core.simple.11

Elf file type is CORE (Core file)
Entry point 0x0
There are 17 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  NOTE           0x00000000000003f8 0x0000000000000000 0x0000000000000000
                 0x0000000000000558 0x0000000000000000         0
  LOAD           0x0000000000001000 0x0000000000400000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R E    1000
  LOAD           0x0000000000002000 0x0000000000600000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000003000 0x00000035fe800000 0x0000000000000000
                 0x0000000000001000 0x000000000001e000  R E    1000
  LOAD           0x0000000000004000 0x00000035fea1d000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R      1000
  LOAD           0x0000000000005000 0x00000035fea1e000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000006000 0x00000035fea1f000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000007000 0x00000035fec00000 0x0000000000000000
                 0x0000000000001000 0x0000000000173000  R E    1000
  LOAD           0x0000000000008000 0x00000035fed73000 0x0000000000000000
                 0x0000000000000000 0x00000000001ff000         1000
  LOAD           0x0000000000008000 0x00000035fef72000 0x0000000000000000
                 0x0000000000004000 0x0000000000004000  R      1000
  LOAD           0x000000000000c000 0x00000035fef76000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x000000000000d000 0x00000035fef77000 0x0000000000000000
                 0x0000000000005000 0x0000000000005000  RW     1000
  LOAD           0x0000000000012000 0x00007fc22db59000 0x0000000000000000
                 0x0000000000003000 0x0000000000003000  RW     1000
  LOAD           0x0000000000015000 0x00007fc22db6c000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000016000 0x00007fff81c40000 0x0000000000000000
                 0x0000000000016000 0x0000000000016000  RW     1000
  LOAD           0x000000000002c000 0x00007fff81dee000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R E    1000
  LOAD           0x000000000002d000 0xffffffffff600000 0x0000000000000000
                 0x0000000000000000 0x0000000000001000  R E    1000

可执行文件中的程序头:

$ readelf --segments simple

Elf file type is EXEC (Executable file)
Entry point 0x400390
There are 8 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001c0 0x00000000000001c0  R E    8
  INTERP         0x0000000000000200 0x0000000000400200 0x0000000000400200
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000674 0x0000000000000674  R E    200000
  LOAD           0x0000000000000678 0x0000000000600678 0x0000000000600678
                 0x00000000000001e4 0x00000000000001f8  RW     200000
  DYNAMIC        0x00000000000006a0 0x00000000006006a0 0x00000000006006a0
                 0x0000000000000190 0x0000000000000190  RW     8
  NOTE           0x000000000000021c 0x000000000040021c 0x000000000040021c
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000005a8 0x00000000004005a8 0x00000000004005a8
                 0x000000000000002c 0x000000000000002c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07

我的 Linux 版本:

$ uname -a
Linux somehost 2.6.32.23-170.fc12.x86_64 #1 SMP Mon Sep 27 17:23:59 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux

【问题讨论】:

  • NT_FILE 注释是在 2012 年添加到内核中的,所以 2010 年构建的内核不会有它。你能得到一个更新的内核吗?
  • 我怀疑在我们需要它工作的生产系统上升级内核不会掉以轻心。但我认为如果 gdb 不需要 NT_FILE 注释来将 rip 寄存器和其他代码地址与 .eh_frame 和 .debug_line 表中的条目相关联,那么必须有另一种方法来计算目标文件到核心的映射图像地址空间。我只需要找出它是什么。
  • 虽然为我们当前的生产系统更新内核可能是不可能的,但我相信我们正在建立新的生产系统,它将基于最近的 CentOS 版本。一旦它们被证明,我们将转移我们的业务。所以我们不会永远被旧内核卡住。仅供参考:)

标签: gdb fedora core dwarf


【解决方案1】:

为什么没有 NT_FILE 注释?

正如 Mark Plotnick 所说,这是一个相当新的内核添加。

GDB 绝不需要 NT_FILE 注释(事实上,当前的 GDB 似乎根本不使用 NT_FILE,除非编写使用 gcore 命令的核心文件)。

如何确定核心文件可能基于的各种目标文件,更重要的是,这些文件映射到核心映像的虚拟地址?

这适用于 GDB 的方法是查看 PT_DYNAMIC 中的主要可执行文件 core,从中提取 DT_DEBUG,然后给它一个指向 _r_debug 的指针,其中包括一个链表 @ struct link_map 中的 987654328@,列表中的每个节点都描述了一个加载的 ELF 文件。

(gdb) info shared 命令将向您显示上述信息的解码版本,但您需要提供匹配的二进制文件:单独的 core 不包含足够的信息。

现在,您的问题不是很清楚,可以通过几种不同的方式来理解。

可能是:“我有一个核心,哪个应用程序崩溃了?”使用file core,希望pathname的前16个字符足够。如果这还不够,运行strings core 通常会显示哪个应用程序产生了它。您还应该考虑将/proc/sys/kernel/core_pattern 设置为包含%e%E 的内容,这样以后这个问题就很容易回答了。

可能是:“我有几个版本的应用程序foo,想知道哪个版本的foo 产生了这个特定的核心”。在这种情况下,您应该将foo-Wl,--build-id 链接器标志链接起来。该标志在foo 二进制文件中创建NT_GNU_BUILD_ID 注释。该注释在strip 中仍然存在,并且也保存在核心文件中。然后您可以运行eu-unstrip -n --core /path/to/core,这将产生如下输出:

eu-unstrip -n --core core
0x400000+0x208000 c266a51e4b85b16ca17bff8328f3abeafb577b29@0x400284 - - [exe]
0x7ffe3f7d9000+0x1000 7f14688f101a2ace5cad23dfbfbc918616651576@0x7ffe3f7d9340 . - linux-vdso.so.1
0x7fb5b6ec3000+0x2241c8 d0f537904076d73f29e4a37341f8a449e2ef6cd0@0x7fb5b6ec31d8 /lib64/ld-linux-x86-64.so.2 /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so ld-linux-x86-64.so.2
0x7fb5b6afe000+0x3c42c0 cf699a15caae64f50311fc4655b86dc39a479789@0x7fb5b6afe280 /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.19.so libc.so.6

从上面的输出中,您可以知道确切使用了哪些 ELF 二进制文件,以及它们在内存中的加载位置。

附:我刚刚尝试从使用-Wl,--build-id=none 构建的a.out 中转储一个核心,得到的eu-unstrip 输出仍然非常有用:

eu-unstrip -n --core core
0x400000+0x202000 - - - [exe]
0x7fff5e1a0000+0x1000 7f14688f101a2ace5cad23dfbfbc918616651576@0x7fff5e1a0340 . - linux-vdso.so.1
0x7fbda432d000+0x2241c8 d0f537904076d73f29e4a37341f8a449e2ef6cd0@0x7fbda432d1d8 /lib64/ld-linux-x86-64.so.2 /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so ld-linux-x86-64.so.2
0x7fbda3f68000+0x3c42c0 cf699a15caae64f50311fc4655b86dc39a479789@0x7fbda3f68280 /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.19.so libc.so.6

更新:

我的核心文件本身没有 PT_DYNAMIC 程序头,

不,但PT_DYNAMIC 是一个可写@0x6006a0。该段实际上是(由动态加载器)写入的,因此始终保存在core 中(作为其他修改的数据)。

在您的情况下,内容位于PT_LOAD@0x600000 中(即core 中文件偏移量0x2000 的段)。

【讨论】:

  • "查看核心中的主要可执行文件的 PT_DYNAMIC" - 我的核心文件本身没有 PT_DYNAMIC 程序头,但你的意思是我应该找到嵌入核心文件中的部分可执行文件并从中解析程序头?我将了解如何将核心文件中的程序头转储添加到问题中。
  • 我可以假设它总是在可执行文件中指定的 vaddr 处加载吗? IE。是否没有可能重新定位此特定数据?如果我还没有识别出可执行文件,如何找到它?
  • @barryd 一个非PIE 可执行文件总是加载到链接地址(否则它不会工作)。我不知道 GDB 如何找到 PIE 二进制文件的动态段。如果没有可执行文件,您将无法找到数据(正如我已经说过的)。
  • 我们一直在使用 psargs 数据(“file”给出)并且还在 core_pattern 中使用 %e。 %e 的长度限制相当短,并且不包含路径。 psargs 足够长,我们的路径/文件名可能不会被截断,尽管参数会。它应该足够好,但我认为可能有一种更强大的方法来确定可执行文件。我猜 build-id 是另一种选择。
  • 你知道 eu-unstrip 是如何从核心文件中获取构建 ID 和加载地址的,而不是提供可执行文件吗?
猜你喜欢
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 2017-01-26
  • 1970-01-01
相关资源
最近更新 更多