Linux: совместное использование backtrace (), / proc / self / maps и addr2line приводит к неверному результату - PullRequest
0 голосов
/ 12 марта 2020

Я пытаюсь реализовать способ записи стеков вызовов моей программы в файл, а затем отобразить его позже. Вот шаги:

  • Записать содержимое / proc / self / maps в файл журнала.
    • В этом примере содержимое / proc / self / maps:
    • 00400000-05cdc000 r-xp 00000000 00:51 12974779926 helloworld
    • Это означает, что базовый адрес программы helloworld равен 0x400000.
  • В программе всякий раз, когда интересному коду необходимо записать свой стек вызовов, я использую функцию backtrace(), чтобы получить адреса вызовов и затем записывать их в файл журнала. Допустим, в этом примере callstack имеет вид:
    • 0x400001
    • 0x400003
  • Через некоторое время, в отдельной программе просмотра журнала, файл журнала открывается и анализируется. Адрес в стеке вызовов будет вычитаться из базового адреса программы. В этом случае:
    • 0x400001 - 0x400000 = 1
  • Затем я использую это вычтенное смещение для получения номера строки с помощью программы addr2line:
    • addr2line -fCe hellowork 0x1
    • Однако это приводит к ??? результату, то есть неверному смещению.
  • Но если я не вычту адрес стека вызовов, а передам фактическое значение команде add2line:
    • addr2line -fCe hellowork 0x400001, затем он возвращает правильный файл и номер строки.

Дело в том, что если адрес внутри общего объекта, то абсолютный адрес выиграл не работает, пока вычитается смещение.

Почему существует такая разница в способе сопоставления адресов для основного исполняемого файла и общих объектов? Или, может быть, это backtrace спецификация реализации c, так что она всегда возвращает абсолютный адрес для функции в основном исполняемом файле?

1 Ответ

0 голосов
/ 14 марта 2020

Почему существует такая разница в способе сопоставления адресов для основного исполняемого файла и общих объектов?

Общие библиотеки обычно связаны по адресу 0 и перемещаются. Непозиционный исполняемый файл обычно связан по адресу 0x400000 по x86_64 Linux и должен не быть перемещен (или не будет работать).

Чтобы узнать, где данный Двоичный файл ELF связан, посмотрите на p_vaddr адрес первого сегмента PT_LOAD (readelf -Wl foo покажет вам это). Кроме того, можно перемещать только двоичные файлы ET_DYN ELF, в то время как двоичные файлы ET_EXEC не должны быть.

Обратите внимание, что существуют независимые от позиции исполняемые файлы, и для них необходимо выполнить вычитание.

Обратите внимание, что совместно используемые библиотеки обычно связаны по адресу 0 (и поэтому вычитание работает), но они не должны . Выполнение prelink в общей библиотеке приведет к тому, что общая библиотека будет связана по адресу, отличному от 0, и тогда вычитаемое вами значение не будет работать либо .

Действительно, что вам нужно do вычитает адрес загрузки во время выполнения из связанного адреса, чтобы получить перемещение (которое будет 0 для не-P IE исполняемых файлов и не 0 для разделяемых библиотек), а затем вычитать это перемещение из счетчика программы, записанного backtrace для получения значения символа.

Наконец, если вы перебираете все загруженные изображения ELF с dl_iterate_phdr , dlpi_addr обеспечивает точно перемещение что вам нужно вычесть.

...