Как сделать так, чтобы карликовые секции загружались в память в файле эльфа? - PullRequest
0 голосов
/ 17 сентября 2018

Я пишу программу на C без стандартной библиотеки, которая загружается в память загрузчиком elf и затем выполняется.Мне бы хотелось, чтобы эта программа на C также имела возможность загружать в память свои отладочные разделы карликов, чтобы она могла печатать обратную трассировку во время выполнения.

Чтобы попытаться добиться этого, я поместил в свою программу на 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_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]

Я не уверен, как это исправить, поскольку разделы действительно существуют только после связывания (?), И, возможно, я смогу настроить lma с помощью objcopy (?), Но тогда я не уверенгде бы я их разместил.

Я видел https://stackoverflow.com/a/31126336/3492895, но я не уверен, как бы я создал "дыру" перед связыванием, чтобы я мог использовать objcopy для настройки.

1 Ответ

0 голосов
/ 18 сентября 2018

Используя предложение user2162550, код удалось скомпилировать, но какой-то код, который мне пришлось распечатать, имена функций, которые были в отладочной информации, ничего не печатали.Затем я увидел комментарий в скрипте компоновщика по умолчанию, который использует gcc (передав ему -Wl,--verbose при компоновке исполняемого файла):

/* DWARF debug sections.
  Symbols in the DWARF debugging sections are relative to the beginning
  of the section so we begin them at 0.  */
...
.debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev   0 : { *(.debug_abbrev) }
...

Это убедило меня, что не имеет значения, где находятся символы отладки,в финальном бинарном файле.Затем я попытался использовать дырочку (из здесь ), но я не был уверен, как скопировать отладочную информацию до того, как исполняемый файл будет связан (как только исполняемый файл будет связан, я не думаю, 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 для INSERT AFTER является произвольным.

Затем я скомпилировал и связал с:

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. Так как это не имело отношения к вопросу, я не включил его. Я использовал это, чтобы убедиться, что там была правильная информация отладки, так как я не нашел никакой магииномера карликов, чтобы искать в Интернете, чтобы обеспечить целостность информации.

Единственная потенциальная проблема заключается в том, что при запуске 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

Но я не понимаю, что этомнеANS, и это не кажется представлять проблему.

Пожалуйста, скажите мне, что я могу сделать, чтобы улучшить этот вопрос или ответ.

...