Linux:同时使用backtrace(),/ proc / self / maps和addr2line导致无效结果

问题描述 投票:0回答:1

我正在尝试实现一种将程序的调用栈记录到文件中,然后再显示的方法。步骤如下:

  • 将/ proc / self / maps的内容写入日志文件。
    • 在此示例中,/ proc / self / maps的内容为:
    • 00400000-05cdc000 r-xp 00000000 00:51 12974779926 helloworld
    • 这意味着helloworld程序的基地址为0x400000。
  • [在程序中,每当需要记录一个有趣的代码的调用栈时,我都会使用函数backtrace()获取该调用栈的地址,然后将其写入日志文件。假设此示例中的调用堆栈为:
    • 0x400001
    • 0x400003
  • 稍后,在单独的日志查看器程序中,将打开并分析日志文件。调用堆栈中的地址将减去程序的基地址。在这种情况下:
    • 0x400001 - 0x400000 = 1
  • 然后,我使用此扣除的偏移量通过addr2line程序获取行号:
    • addr2line -fCe hellowork 0x1
    • 但是这会产生???结果,即无效的偏移量。
  • 但是如果我不扣除调用栈的地址,而是将实际值传递给add2line命令:
    • addr2line -fCe hellowork 0x400001,然后返回正确的文件和行号。

事情是,如果地址在共享库中,则绝对地址将不起作用,而扣除的偏移量将起作用。

为什么为主要可执行文件和共享对象映射地址的方式如此不同?也许这是特定于backtrace实现的,因此它总是为主要可执行文件中的函数返回绝对地址?

c linux elf backtrace
1个回答
0
投票

为什么为主要可执行文件和共享对象映射地址的方式如此不同?

共享库通常链接到地址0并重新定位。在x86_64 Linux上,非位置可执行文件通常链接在地址0x400000上,必须not进行重定位(否则它将无法工作)。

[要找出给定的ELF二进制文件链接的位置,请查看第一个p_vaddr段的PT_LOAD地址(readelf -Wl foo会向您显示)。另外,只能重定位ET_DYN ELF二进制文件,而不能重定位ET_EXEC二进制文件。

请注意,存在与位置无关的可执行文件,对于它们,您需要进行减法。

请注意,共享库通常在地址0处链接(因此减法有效),但它们不是必须。在共享库上运行prelink将导致在非0地址处链接共享库,因此您使用的减法将不起作用两个

确实,您需要做的是从链接地址中减去运行时加载地址以获取重定位(对于非PIE可执行文件为0,对于共享库为0),然后从backtrace记录的程序计数器以获取符号值。

最后,如果用dl_iterate_phdr迭代所有加载的ELF图像,则它提供的dlpi_addr精确地您需要减去的重定位。

© www.soinside.com 2019 - 2024. All rights reserved.