【问题标题】:gdb add-symbol-file all sections and load addressgdb add-symbol-file 所有部分和加载地址
【发布时间】:2016-01-08 01:08:53
【问题描述】:

我正在使用 gdb 和 qemu 的 gdb-stub 调试引导加载程序 (syslinux)。在某些时候,主文件会加载一个共享对象ldlinux.elf

我想在 gdb 中为该文件添加符号。命令add-symbol-file 似乎是要走的路。但是,作为可重定位文件,我必须指定它已加载的内存地址。问题来了。

虽然我知道LOAD 段已加载的基地址,但add-symbol-file 按段工作,并希望我指定每个段已加载的地址。

如果我在内存中指定了文件的基地址,我可以告诉 gdb 加载所有节的所有符号吗?

gdb 的行为有意义吗?节标题不用于运行 ELF,甚至是可选的。我看不到指定部分的加载地址有用的用例。


示例

这里是共享对象的程序头和节头。

Elf 文件类型为 DYN(共享对象文件) 入口点 0x4c60 有 3 个程序头,从偏移量 52 开始 程序标题: 类型 偏移 VirtAddr PhysAddr FileSiz MemSiz Flg Align 加载 0x000000 0x00000000 0x00000000 0x1db10 0x20bfc RWE 0x1000 动态 0x01d618 0x0001d618 0x0001d618 0x00098 0x00098 RW 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 段到段映射: 分段部分... 00 .gnu.hash .dynsym .dynstr .rel.dyn .rel.plt .plt .text .rodata .ctors .dtors .data.rel.ro .dynamic .got .got.plt .data .bss 01.动态 02 有 29 个节头,从偏移量 0x78618 开始: 部分标题: [编号] Name Type Addr Off Size ES Flg Lk Inf Al [0]空00000000 000000 000000 00 0 0 0 [1].gnu.hash GNU_HASH 00000094 000094 0007e0 04 A 2 0 4 [2].dynsym 动态 00000874 000874 0015c0 10 A 3 1 4 [3].dynstr STRTAB 00001e34 001e34 0010f4 00 A 0 0 1 [4].rel.dyn REL 00002f28 002f28 000ce8 08 A 2 0 4 [5].rel.plt REL 00003c10 003c10 000568 08 AI 2 6 4 [6] .plt 程序 00004180 004180 000ae0 04 AX 0 0 16 [ 7] .text PROGBITS 00004c60 004c60 013816 00 AX 0 0 4 [8] .rodata PROGBITS 00018480 018480 00462f 00 A 0 0 32 [9].ctors INIT_ARRAY 0001cab0 01cab0 000010 00 WA 0 0 4 [10] .dtors FINI_ARRAY 0001cac0 01cac0 000004 00 WA 0 0 4 [11] .data.rel.ro 程序 0001cae0 01cae0 000b38 00 WA 0 0 32 [12] .动态动态 0001d618 01d618 000098 08 WA 3 0 4 [13] .got PROGBITS 0001d6b0 01d6b0 0000d0 04 WA 0 0 4 [14] .got.plt 程序 0001d780 01d780 0002c0 04 WA 0 0 4 [15] .data PROGBITS 0001da40 01da40 0000d0 00 WA 0 0 32 [16] .bss NOBITS 0001db20 01db10 0030dc 00 WA 0 0 32 [17] .comment PROGBITS 00000000 01db10 000026 01 MS 0 0 1 [18] .debug_aranges 程序 00000000 01db38 0010c0 00 0 0 8 [19] .debug_info 程序 00000000 01ebf8 021ada 00 0 0 1 [20] .debug_abbrev 程序 00000000 0406d2 009647 00 0 0 1 [21] .debug_line 程序 00000000 049d19 00bd3a 00 0 0 1 [22] .debug_frame 程序 00000000 055a54 004574 00 0 0 4 [23] .debug_str 程序 00000000 059fc8 00538c 01 MS 0 0 1 [24] .debug_loc 程序 00000000 05f354 01312d 00 0 0 1 [25] .debug_ranges 程序 00000000 072481 0005d0 00 0 0 1 [26] .shstrtab STRTAB 00000000 072a51 000101 00 0 0 1 [27] .symtab SYMTAB 00000000 072b54 003530 10 28 504 4 [28] .strtab STRTAB 00000000 076084 002593 00 0 0 1 标志的关键: W(写入),A(分配),X(执行),M(合并),S(字符串) I(信息),L(链接顺序),G(组),T(TLS),E(排除),x(未知) O(需要额外的操作系统处理)o(特定于操作系统),p(特定于处理器)

如果我尝试在地址 0x7fab000 加载文件,那么它将重新定位符号,以便 .text 部分从 0x7fab000 开始。

(gdb) 添加符号文件 bios/com32/elflink/ldlinux/ldlinux.elf 0x7fab000 从文件“bios/com32/elflink/ldlinux/ldlinux.elf”中添加符号表 .text_addr = 0x7fab000 (是或否)是 从 bios/com32/elflink/ldlinux/ldlinux.elf 读取符号...完成。

然后所有的符号都被 0x4c60 字节关闭。

【问题讨论】:

    标签: gdb debug-symbols


    【解决方案1】:

    所以,最后,我使用 python 和readelf 工具创建了自己的命令。它不是很干净,因为它在子进程中运行 readelf 并解析其输出而不是直接解析 ELF 文件,但它可以工作(仅适用于 32 位 ELF)。

    它使用节标题生成并运行add-symbol-file 命令,所有节都正确重定位。用法很简单,你给它elf文件和文件的基地址。由于 remove-symbol-file 仅通过提供文件名就无法正常工作,因此我创建了一个 remove-symbol-file-all 来生成并运行正确的 remove-symbol-file -a address 命令。

    (gdb) add-symbol-file-all bios/com32/elflink/ldlinux/ldlinux.elf 0x7fab000
    add symbol table from file "bios/com32/elflink/ldlinux/ldlinux.elf" at
            .text_addr = 0x7fafc50
            .gnu.hash_addr = 0x7fab094
            .dynsym_addr = 0x7fab874
            .dynstr_addr = 0x7face34
            .rel.dyn_addr = 0x7fadf28
            .rel.plt_addr = 0x7faec08
            .plt_addr = 0x7faf170
            .rodata_addr = 0x7fc34e0
            .ctors_addr = 0x7fc7af0
            .dtors_addr = 0x7fc7b00
            .data.rel.ro_addr = 0x7fc7b20
            .dynamic_addr = 0x7fc8658
            .got_addr = 0x7fc86f0
            .got.plt_addr = 0x7fc87bc
            .data_addr = 0x7fc8a80
            .bss_addr = 0x7fc8b60
    (gdb) remove-symbol-file-all bios/com32/elflink/ldlinux/ldlinux.elf 0x7fab000
    

    这是要添加到.gdbinit 文件中的代码。

    python
    import subprocess
    import re
    
    def relocatesections(filename, addr):
        p = subprocess.Popen(["readelf", "-S", filename], stdout = subprocess.PIPE)
    
        sections = []
        textaddr = '0'
        for line in p.stdout.readlines():
            line = line.decode("utf-8").strip()
            if not line.startswith('[') or line.startswith('[Nr]'):
                continue
    
            line = re.sub(r' +', ' ', line)
            line = re.sub(r'\[ *(\d+)\]', '\g<1>', line)
            fieldsvalue = line.split(' ')
            fieldsname = ['number', 'name', 'type', 'addr', 'offset', 'size', 'entsize', 'flags', 'link', 'info', 'addralign']
            sec = dict(zip(fieldsname, fieldsvalue))
    
            if sec['number'] == '0':
                continue
    
            sections.append(sec)
    
            if sec['name'] == '.text':
                textaddr = sec['addr']
    
        return (textaddr, sections)
    
    
    class AddSymbolFileAll(gdb.Command):
        """The right version for add-symbol-file"""
    
        def __init__(self):
            super(AddSymbolFileAll, self).__init__("add-symbol-file-all", gdb.COMMAND_USER)
            self.dont_repeat()
    
        def invoke(self, arg, from_tty):
            argv = gdb.string_to_argv(arg)
            filename = argv[0]
    
            if len(argv) > 1:
                offset = int(str(gdb.parse_and_eval(argv[1])), 0)
            else:
                offset = 0
    
            (textaddr, sections) = relocatesections(filename, offset)
    
            cmd = "add-symbol-file %s 0x%08x" % (filename, int(textaddr, 16) + offset)
    
            for s in sections:
                addr = int(s['addr'], 16)
                if s['name'] == '.text' or addr == 0:
                    continue
    
                cmd += " -s %s 0x%08x" % (s['name'], addr + offset)
    
            gdb.execute(cmd)
    
    class RemoveSymbolFileAll(gdb.Command):
        """The right version for remove-symbol-file"""
    
        def __init__(self):
            super(RemoveSymbolFileAll, self).__init__("remove-symbol-file-all", gdb.COMMAND_USER)
            self.dont_repeat()
    
        def invoke(self, arg, from_tty):
            argv = gdb.string_to_argv(arg)
            filename = argv[0]
    
            if len(argv) > 1:
                offset = int(str(gdb.parse_and_eval(argv[1])), 0)
            else:
                offset = 0
    
            (textaddr, _) = relocatesections(filename, offset)
    
            cmd = "remove-symbol-file -a 0x%08x" % (int(textaddr, 16) + offset)
            gdb.execute(cmd)
    
    
    AddSymbolFileAll()
    RemoveSymbolFileAll()
    end
    

    【讨论】:

    • 您可以做一个很小的改进:在调用 readelf 时添加 -W,因为有些库的大段名称会被截断(例如 /lib/x86_64-linux-gnu/libnss_compat- 2.27.so -> .note.gnu.build-id)。
    【解决方案2】:

    如果我指定了内存中文件的基地址,我可以告诉 gdb 加载所有部分的所有符号吗?

    可以,但您需要在此处提供.text 部分的地址,即0x7fab000+0x00004c60。我同意:不得不把.text的地址找出来很烦人,我想修复它很多次,所以例如

    (gdb) add-symbol-file foo.so @0x7abc0000
    

    只是工作。请随时在GDB bugzilla 中提出功能请求。

    gdb 的行为有意义吗?

    我猜这源于 GDB 用于调试嵌入式 ROM 的方式,其中每个部分都可以位于任意内存地址。

    【讨论】:

    • 如果我想添加数据的符号,我必须添加.data.rodata.bss部分的选项和地址。如果我有特定的东西要调试,也许更多。我现在正在编写一个 pythong 脚本来生成正确的 gdb 命令。
    • @Celelibi 否:通常添加foo.so.text 地址而不是加载地址是all 你需要.text .data.
    • GDB 投诉:A syntax error in expression, near '0x7abc000'.
    • @Yorkwar 您在抱怨我想要修复的缺失功能。我还没有修复它,所以当然 GDB 会抱怨。
    • @EmployedRussian 你有没有添加过这个功能?
    猜你喜欢
    • 1970-01-01
    • 2015-11-18
    • 2014-02-25
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多