C 如何使dwarf部分加载到elf文件的内存中?
我正在编写一个没有标准库的C程序,它由elf加载程序加载到内存中,然后执行。我希望这个C程序也能够将其dwarf调试部分加载到内存中,这样它就可以在运行时打印回溯 为了实现这一点,我在我的C程序中设置了:C 如何使dwarf部分加载到elf文件的内存中?,c,linker,elf,dwarf,C,Linker,Elf,Dwarf,我正在编写一个没有标准库的C程序,它由elf加载程序加载到内存中,然后执行。我希望这个C程序也能够将其dwarf调试部分加载到内存中,这样它就可以在运行时打印回溯 为了实现这一点,我在我的C程序中设置了: extern char __my_old_debug_abbrev_start[]; extern char __my_old_debug_abbrev_end[]; extern char __my_old_debug_info_start[]; extern char __my_old_d
extern char __my_old_debug_abbrev_start[];
extern char __my_old_debug_abbrev_end[];
extern char __my_old_debug_info_start[];
extern char __my_old_debug_info_end[];
extern char __my_old_debug_str_start[];
extern char __my_old_debug_str_end[];
这样它就能知道这些部分在哪里。然后,为了实际提供位置,我有一个链接器脚本,如下所示:
SECTIONS
{
.debug_abbrev : {
__my_old_debug_abbrev_start = .;
KEEP (*(.debug_abbrev)) *(.debug_abbrev)
__my_old_debug_abbrev_end = .;
}
.debug_info : {
__my_old_debug_info_start = .;
KEEP (*(.debug_info .gnu.linkonce.wi.*)) *(.debug_info .gnu.linkonce.wi.*)
__my_old_debug_info_end = .;
}
.debug_str : {
__my_old_debug_str_start = .;
KEEP (*(.debug_str)) *(.debug_str)
__my_old_debug_str_end = .;
}
}
INSERT AFTER .rodata;
首先,我将C程序编译成一个libtest.a
,然后使用objcopy
将节设置为alloc
和load
objcopy --set-section-flags '.debug_abbrev=alloc,load' libtest.a
objcopy --set-section-flags '.debug_info=alloc,load' libtest.a
objcopy --set-section-flags '.debug_str=alloc,load' libtest.a
objcopy --set-section-flags '.gnu.linkonce.wi.*=alloc,load' libtest.a
然后,我在归档文件上运行gcc,将其编译成可执行文件,大致如下:
gcc libtest.a -o test -T test.lds -static
这会产生错误:
/usr/bin/x86_64-linux-gnu-ld: section .debug_info LMA [0000000000000000,0000000000066291] overlaps section .debug_abbrev LMA [0000000000000000,0000000000007cce]
/usr/bin/x86_64-linux-gnu-ld: section .debug_str LMA [0000000000000000,000000000009d264] overlaps section .debug_info LMA [0000000000000000,0000000000066291]
我不确定如何解决这个问题,因为这些部分只有在链接(?)之后才真正存在,然后我可能可以使用objcopy
(?)调整lma,但我不确定将它们放在哪里
我已经看到了,但我不确定如何在链接之前创建“洞”,以便我可以使用
objcopy
来调整内容。根据user2162550的建议,代码成功编译,但我不得不打印出调试信息中的函数名,但没有打印出任何内容。然后,我在gcc使用的默认链接器脚本中看到了一条注释(在链接可执行文件时将-Wl,--verbose
传递给它):
这让我确信调试符号在最终二进制文件中的位置并不重要。因此,我尝试使用“洞”技巧(来自),但在链接可执行文件之前,我不确定如何复制调试信息(一旦链接可执行文件,我认为objcopy
不再有效)。因此,我决定在二进制文件中保留一些加载和分配的空间,然后在链接之后,将所需的部分复制到该空间中
为此,我使用链接器脚本留下了一个漏洞,并提供了一些符号来确定调试部分的位置。我使用的方法是使用链接器脚本首先测量每个调试部分的大小,然后为其分配足够的空间。这看起来像(在test.lds
中):
/* This finds the start and end of each section so we know its size */
SECTIONS
{
.debug_info 0 : {
__my_old_debug_info_start = .;
KEEP (*(.debug_info .gnu.linkonce.wi.*)) *(.debug_info .gnu.linkonce.wi.*)
__my_old_debug_info_end = .;
}
.debug_abbrev 0 : {
__my_old_debug_abbrev_start = .;
KEEP (*(.debug_abbrev)) *(.debug_abbrev)
__my_old_debug_abbrev_end = .;
}
.debug_str 0 : {
__my_old_debug_str_start = .;
KEEP (*(.debug_str)) *(.debug_str)
__my_old_debug_str_end = .;
}
}
INSERT AFTER .rodata;
/* This creates some space in the binary which is loaded and big enough to store all the debugging info, as well as marking the start and end of each area */
SECTIONS
{
.debug_all : {
__my_debug_info_start = .;
. += __my_old_debug_info_end - __my_old_debug_info_start;
__my_debug_info_end = .;
__my_debug_abbrev_start = .;
. += __my_old_debug_abbrev_end - __my_old_debug_abbrev_start;
__my_debug_abbrev_end = .;
__my_debug_str_start = .;
. += __my_old_debug_str_end - __my_old_debug_str_start;
__my_debug_str_end = .;
}
}
INSERT AFTER .rodata;
我认为在之后插入时选择.rodata
是任意的
然后,我编译并链接到:
gcc libtest.a -g -o test -T test.lds -static
受此启发,我让一个bash脚本解析readelf
的输出,并计算二进制文件中的何处来获取调试信息,以及将其复制到何处以便加载。复制是使用dd
完成的
function getSymbolValue {
binary=$1
symbol=$2
# Assumes that this will only find one symbol
truncated_symbol=`echo $symbol | cut -c 1-25`
readelf -s $binary | grep $truncated_symbol | awk '{print $2}'
}
function getSectionInfo {
binary=$1
section=$2
# returns all but the [Nr] column of data returned by readelf
# https://stackoverflow.com/a/3795522/3492895
readelf -S $binary | cut -c7- | grep '\.'"$section"
}
function getSectionAddress {
binary=$1
section=$2
getSectionInfo $binary $section | awk '{print $3}'
}
function getSectionOffset {
binary=$1
section=$2
getSectionInfo $binary $section | awk '{print $4}'
}
function copyData {
binary=$1
from_start=$2
to_start=$3
len=$4
dd iflag=skip_bytes,count_bytes if=$binary skip=$from_start count=$len | dd oflag=seek_bytes of=$binary seek=$to_start count=$len conv=notrunc
}
function copyDebugSection {
binary=$1
from_section=$2
to_section=$3
from_off=`getSectionOffset $binary $from_section`
to_section_off=`getSectionOffset $binary $to_section`
to_section_addr=`getSectionAddress $binary $to_section`
to_start_addr=`getSymbolValue $binary "__my_${from_section}_start"`
to_end_addr=`getSymbolValue $binary "__my_${from_section}_end"`
copyData $binary $((0x$from_off)) $((0x$to_start_addr - 0x$to_section_addr + 0x$to_section_off)) $((0x$to_end_addr - 0x$to_start_addr))
}
copyDebugSection ./test 'debug_info' 'debug_all'
copyDebugSection ./test 'debug_abbrev' 'debug_all'
copyDebugSection ./test 'debug_str' 'debug_all'
运行这个之后,我期望的函数名就被打印出来了
如果有人想知道我是如何打印出函数名的,我使用库gimli编写了一些rust代码。因为这与问题无关,所以我没有包含它。我使用它来确保正确的调试信息在那里,因为我没有在网上找到任何魔幻矮人数字来确保信息的完整性离子
唯一的潜在问题是,当运行readelf
时,它会输出:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
...
readelf: Warning: [ 3]: Link field (0) should index a symtab section.
[ 3] .rela.plt RELA 0000000000400168 00000168
0000000000000228 0000000000000018 AI 0 25 8
但我不明白这意味着什么,而且似乎也不构成问题
请告诉我是否有什么可以改进此问题或答案。尝试将三个输出部分合并为一个,称之为.debug\u all
,然后发布结果您可以通过声明(并获取的地址)获得调试部分的开始和结束类似于extern void\uu start\u debug\u info,\uu stop\u debug\u info;
以避免符号放置的恶作剧。请看另一个问题:@zneak我试过了,在链接器脚本中替换+==\uu my\u old\u debug\u info\u end-\uu my\u old\u debug\u info\u start;
和+=\uu stop\u debug\u info-\uu start\u debug\u info;
等,然后重新启动移动第一个节
块。链接器弹出:`undefined symbol\uuuu stop\u debug\u info'在表达式
中引用,因此它似乎不起作用。D=如果从C代码中使用它们,而不是将它们集成到链接器脚本中,会发生什么问题?那么,您的节没有复制到可执行文件中吗?我想然后问题将变成计算自定义.debug\u all
节应该有多大,以包含调试节的副本。我尝试使用SIZEOF
但似乎不起作用。如果.debug\u all
是在.rodata之后插入,但如果我们在最后一个调试节之后插入它,则返回0启用时,它返回一个非零值。但随后readelf
报告警告:偏移量0xb处的DIE指的是不存在的缩写数字1
,调试部分似乎无法正确解析。
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
...
readelf: Warning: [ 3]: Link field (0) should index a symtab section.
[ 3] .rela.plt RELA 0000000000400168 00000168
0000000000000228 0000000000000018 AI 0 25 8