GDB add-symbol-file все разделы и адрес загрузки
Я отлаживаю загрузчик (syslinux) с помощью gdb и заглушки gdb из qemu. В какой-то момент основной файл загружает общий объект ldlinux.elf
,
Я хотел бы добавить символы в GDB для этого файла. Команда add-symbol-file
кажется, путь. Однако, как перемещаемый файл, я должен указать адрес памяти, в который он был загружен. И тут возникает проблема.
Хотя я знаю базовый адрес, по которому LOAD
сегмент был загружен в, add-symbol-file
работает по разделам и хочет, чтобы я указал адрес, по которому каждый раздел был загружен.
Могу ли я сказать gdb загрузить все символы всех разделов, если я укажу базовый адрес файла в памяти?
Имеет ли смысл поведение GDB? Заголовки разделов не используются для запуска ELF и даже являются необязательными. Я не вижу варианта использования, в котором было бы полезно указать адрес загрузки разделов.
пример
Вот заголовки программ и заголовки разделов общего объекта.
Тип файла elf: DYN (общий объектный файл). Точка входа 0x4c60. Имеется 3 заголовка программы, начиная со смещения. 52 Заголовки программы: Тип смещения. VirtAddr PhysAddr FileSiz MemSiz Flg. 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 .dynamic 02
Существует 29 заголовков разделов, начиная со смещения 0x78618: Заголовки разделов: [Nr] Имя Тип Адреса Выкл. Размер ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .gnu.hash GNU_HASH 00000094 000094 0007e0 04 A 2 0 4 [ 2] .dynsym 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 PROGBITS 00004180 004180 000ae0 04 AX 0 0 16 [ 7] .text PROGBITS 00004c60 004c60 013816 00 AX 0 0 4 [ 8] .rodata PROGBITS 00018480 0184 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 32 [12].dynamic DYNAMIC 0001d618 01d618 000098 08 WA 3 0 4 [13] .got PROGBITS 0001d6b0 01d6b0 0000d0 04 WA 0 0 4 [14] .got.plt PROGBITS 0001d780 01d780 0002c0 04 WA 0 0 4 [15]. данные 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 PROGBITS 00000000 01db38 0010c0 00 0 0 8 [19] .debug_info PROGB 01ebf8 021ada 00 0 0 1 [20] .debug_abbrev PROGBITS 00000000 0406d2 009647 00 0 0 1 [21] .debug_line PROGBITS 00000000 049d19 00bd3a 00 0 0 1 22 [. PROGBITS 00000000 059fc8 00538c 01 MS 0 0 1 [24] .debug_loc PROGBITS 00000000 05f354 01312d 00 0 0 1 [25] .debug_ranges ] .symtab SYMTAB 00000000 072b54 003530 10 28 504 4 [28] .strtab STRTAB 00000000 076084 002593 00 0 0 1 Ключ к флагам: W (запись), A (alloc), X (выполнение), M (объединение), S (строки) I (информация), L (порядок ссылок), G (группа), T (TLS), E (исключить), x (неизвестно) O (требуется дополнительная обработка ОС) o (зависит от ОС), p (зависит от процессора)
Если я попытаюсь загрузить файл по адресу 0x7fab000
тогда он будет перемещать символы так, чтобы .text
раздел начинается с 0x7fab000.
(gdb) add-symbol-file bios / com32 / elflink / ldlinux / ldlinux.elf 0x7fab000 добавить таблицу символов из файла "bios/com32/elflink/ldlinux/ldlinux.elf" в.text_addr = 0x7fab000 (у или н) у Чтение символов из bios/com32/elflink/ldlinux/ldlinux.elf... сделано.
И тогда все символы отключены на 0x4c60 байтов.
2 ответа
Итак, наконец, я сделал свою собственную команду с 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
Могу ли я сказать gdb загрузить все символы всех разделов, если я укажу базовый адрес файла в памяти?
Да, но вам нужно предоставить адрес .text
раздел, т.е. 0x7fab000+0x00004c60
Вот. Я согласен: это довольно раздражает, чтобы выловить адрес .text
и я хотел это исправить много раз, так что, например,
(gdb) add-symbol-file foo.so @0x7abc0000
просто работает. Не стесняйтесь подать запрос на функцию в GDB bugzilla.
Имеет ли смысл поведение GDB?
Я предполагаю, что это основано на том, как GDB использовался для отладки встроенных ПЗУ, где каждый раздел может быть по произвольному адресу памяти.