我正在尝试实现一种将程序的调用栈记录到文件中,然后再显示的方法。步骤如下:
00400000-05cdc000 r-xp 00000000 00:51 12974779926 helloworld
helloworld
程序的基地址为0x400000。backtrace()
获取该调用栈的地址,然后将其写入日志文件。假设此示例中的调用堆栈为:0x400001 - 0x400000 = 1
addr2line -fCe hellowork 0x1
???
结果,即无效的偏移量。addr2line -fCe hellowork 0x400001
,然后返回正确的文件和行号。事情是,如果地址在共享库中,则绝对地址将不起作用,而扣除的偏移量将起作用。
为什么为主要可执行文件和共享对象映射地址的方式如此不同?也许这是特定于backtrace
实现的,因此它总是为主要可执行文件中的函数返回绝对地址?
为什么为主要可执行文件和共享对象映射地址的方式如此不同?
共享库通常链接到地址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
是精确地您需要减去的重定位。