Как разобрать одну единственную функцию с помощью objdump?

В моей системе установлен двоичный файл, и я хотел бы посмотреть на дизассемблирование данной функции. Предпочтительно использовать objdump, но допустимы и другие решения.

Из этих вопросов я узнал, что мог бы разобрать часть кода, если бы знал только граничные адреса. Из этот ответ я узнал, как превратить разделенные отладочные символы обратно в один файл.

Но даже работая с этим единственным файлом и даже дизассемблируя весь код (то есть без начального или конечного адреса, но с простым параметром -d для objdump), я все равно нигде не вижу этого символа. Это имеет смысл, поскольку рассматриваемая функция является статической, поэтому она не экспортируется. Тем не менее, valgrind сообщит имя функции, поэтому его нужно где-то хранить.

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


person MvG    schedule 31.03.2014    source источник
comment
Небольшое замечание: если функция помечена static, она может быть встроена компилятором в свои сайты вызова. Это может означать, что на самом деле не может быть никакой функции для дизассемблирования per se. Если вы можете найти символы для других функций, но не для функции, которую вы ищете, это сильный намек на то, что функция была встроена. Valgrind может по-прежнему ссылаться на исходную предварительно встроенную функцию, поскольку в отладочной информации файла ELF хранится информация о том, откуда была взята каждая отдельная инструкция, даже если инструкции перемещены в другое место.   -  person davidg    schedule 01.04.2014
comment
@davidg: правда, но поскольку ответ Тома сработал в этом случае, похоже, это не так. Тем не менее, знаете ли вы способ, например. аннотировать ассемблерный код информацией о том, откуда взялась каждая инструкция?   -  person MvG    schedule 01.04.2014
comment
Рад слышать! addr2line примет ПК/IP-адреса от stdin и распечатает соответствующие строки исходного кода. Точно так же objdump -l будет смешивать objdump со строками исходного кода; хотя для высокооптимизированного кода с тяжелым встраиванием результаты любой программы не всегда особенно полезны.   -  person davidg    schedule 01.04.2014


Ответы (8)


Я бы предложил использовать gdb как самый простой подход. Вы даже можете сделать это как однострочный, например:

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
person Tom Tromey    schedule 01.04.2014
comment
+1 недокументированная функция! -ex 'command' отсутствует в man gdb!? Но на самом деле он указан в документах gdb. . Кроме того, для других такие вещи, как /bin/ls, могут быть удалены, поэтому, если эта точная команда ничего не отображает, попробуйте другой объект! Также можно указать файл/объект в качестве аргумента голого слова; например, gdb -batch -ex 'disassemble main' /bin/ls - person hoc_age; 17.10.2014
comment
Страница руководства не является окончательной. Долгое время он не поддерживался, но теперь я думаю, что он создан из основных документов. Кроме того, gdb --help теперь более полный. - person Tom Tromey; 18.10.2014
comment
gdb /bin/ls -batch -ex 'disassemble main' тоже работает - person stefanct; 21.09.2016
comment
Если вы используете column -ts$'\t' для фильтрации вывода GDB, у вас будут хорошо выровненные необработанные байты и исходные столбцы. Кроме того, -ex 'set disassembly-flavor intel' перед другими -ex приводит к синтаксису ассемблера Intel. - person Ruslan; 04.10.2018
comment
Я вызвал disassemble fn, используя описанный выше метод. Но кажется, что когда в бинарном файле есть несколько функций с одинаковым именем, дизассемблируется только одна. Можно ли дизассемблировать их все или я должен дизассемблировать их на основе необработанного адреса? - person TheAhmad; 25.01.2020
comment
Просто добавьте, что если у вас есть символ в пространстве имен, вам нужно поместить его имя в одинарные кавычки: gdb -batch -ex 'file binary' -ex "disassemble 'namespace::function'" В противном случае я получаю запутанную ошибку No type "function" within class or namespace "namespace".. Если я использую двойные кавычки, я получаю You can't do that without a process to debug., что также сбивает с толку. - person Simon; 26.08.2020

gdb disassemble/rs для отображения исходных и необработанных байтов

С этим форматом он очень близок к выводу objdump -S:

gdb -batch -ex "disassemble/rs $FUNCTION" "$EXECUTABLE"

main.c

#include <assert.h>

int myfunc(int i) {
    i = i + 2;
    i = i * 2;
    return i;
}

int main(void) {
    assert(myfunc(1) == 6);
    assert(myfunc(2) == 8);
    return 0;
}

Компилировать и дизассемблировать

gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
gdb -batch -ex "disassemble/rs myfunc" main.out

Разборка:

Dump of assembler code for function myfunc:
main.c:
3       int myfunc(int i) {
   0x0000000000001135 <+0>:     55      push   %rbp
   0x0000000000001136 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000001139 <+4>:     89 7d fc        mov    %edi,-0x4(%rbp)

4           i = i + 2;
   0x000000000000113c <+7>:     83 45 fc 02     addl   $0x2,-0x4(%rbp)

5           i = i * 2;
   0x0000000000001140 <+11>:    d1 65 fc        shll   -0x4(%rbp)

6           return i;
   0x0000000000001143 <+14>:    8b 45 fc        mov    -0x4(%rbp),%eax

7       }
   0x0000000000001146 <+17>:    5d      pop    %rbp
   0x0000000000001147 <+18>:    c3      retq   
End of assembler dump.

Протестировано на Ubuntu 16.04, GDB 7.11.1.

обходные пути objdump + awk

Распечатайте абзац, как указано по адресу: https://unix.stackexchange.com/questions/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-что-имеет-текст

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <FUNCTION>/'

e.g.:

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <myfunc>/'

дает только:

0000000000001135 <myfunc>:
    1135:   55                      push   %rbp
    1136:   48 89 e5                mov    %rsp,%rbp
    1139:   89 7d fc                mov    %edi,-0x4(%rbp)
    113c:   83 45 fc 02             addl   $0x2,-0x4(%rbp)
    1140:   d1 65 fc                shll   -0x4(%rbp)
    1143:   8b 45 fc                mov    -0x4(%rbp),%eax
    1146:   5d                      pop    %rbp
    1147:   c3                      retq   

При использовании -S я не думаю, что существует безотказный способ, так как комментарии к коду могут содержать любую возможную последовательность... Но следующее работает почти всегда:

objdump -S main.out | awk '/^[[:xdigit:]]+ <FUNCTION>:$/{flag=1;next}/^[[:xdigit:]]+ <.*>:$/{flag=0}flag'

адаптировано из: How для выбора строк между двумя шаблонами маркеров, которые могут встречаться несколько раз с помощью awk/sed

Ответы на список рассылки

В списке рассылки есть тема 2010 года, в которой говорится, что это невозможно: https://sourceware.org/ml/binutils/2010-04/msg00445.html

Помимо обходного пути gdb, предложенного Томом, они также комментируют другой (худший) обходной путь компиляции с -ffunction-section, который помещает одну функцию в раздел, а затем выгружает раздел.

Николас Клифтон дал ему WONTFIX https://sourceware.org/ml/binutils/2015-07/msg00004.html , вероятно, потому, что обходной путь GDB охватывает этот вариант использования.

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 30.06.2015
comment
Подход gdb прекрасно работает с разделяемыми библиотеками и объектными файлами. - person Tom Tromey; 04.08.2015

Дизассемблируйте одну единственную функцию с помощью Objdump

У меня есть два решения:

1. На основе командной строки

Этот метод отлично работает и более простой. Я использую objdump с флагом -d и передаю его через awk. Разобранный вывод выглядит так

000000000000068a <main>:
68a:    55                      push   %rbp
68b:    48 89 e5                mov    %rsp,%rbp
68e:    48 83 ec 20             sub    $0x20,%rsp

Для начала я начну с описания вывода objdump. Раздел или функция отделяются пустой строкой. Таким образом, изменение FS (разделитель полей) на новую строку и RS (разделитель записей) на двойную новую строку позволяет легко искать рекомендуемую функцию, поскольку ее просто найти внутри поле $1!

objdump -d name_of_your_obj_file | awk -F"\n" -v RS="\n\n" '$1 ~ /main/'

Конечно, вы можете заменить main любой другой функцией, которую хотите напечатать.

2. Баш-скрипт

Я написал небольшой скрипт bash для этой проблемы. Вставьте и скопируйте его и сохраните как, например. файл dasm.

#!/bin/bash
# Author: abu
# filename: dasm
# Description: puts disassembled objectfile to std-out

if [ $# = 2 ]; then
        sstrg="^[[:xdigit:]]{2,}+.*<$2>:$"
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '$1 ~ /'"$sstrg"'/'
elif [ $# = 1 ]; then
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '{ print $1 }'
else
    echo "You have to add argument(s)"
    echo "Usage:   "$0 " arg1 arg2"  
    echo "Description: print disassembled label to std-out"
    echo "             arg1: name of object file"
    echo "             arg2: name of function to be disassembled"
    echo "         "$0 " arg1    ... print labels and their rel. addresses" 
fi

Измените x-access и вызовите его, например:

chmod +x dasm
./dasm test main

Это намного быстрее, чем вызов gdb с помощью скрипта. Кроме того, использование objdump не загружает библиотеки в память и поэтому безопаснее!


Виталий Фадеев запрограммировал автодополнение к этому скрипту, что очень удобно и ускоряет набор текста.

Скрипт можно найти здесь.

person abu_bua    schedule 09.04.2018
comment
Кажется, это зависит от того, быстрее ли objdump или gdb. Для огромного двоичного файла (libxul.so Firefox) objdump занимает вечность, я отменил его через час, а gdb занимает меньше минуты. - person Simon; 26.08.2020

Если у вас самая последняя версия binutils (2.32+), это очень просто.

Передача --disassemble=SYMBOL в objdump приведет к дизассемблированию только указанной функции. Нет необходимости передавать начальный адрес и конечный адрес.

LLVM objdump также имеет аналогичную опцию (--disassemble-symbols).

person Léo Lam    schedule 06.06.2020
comment
Спасибо. Список изменений для binutils 2.32, 02 февраля 2019 г.: lists.gnu .org/archive/html/info-gnu/2019-02/msg00000.html Опция Objdump --disassemble теперь может принимать параметр, указывающий начальный символ для дизассемблирования. Дизассемблирование продолжится от этого символа до следующего символа или конца функции. - person osgx; 30.09.2020

Чтобы упростить использование awk для анализа вывода objdump относительно других ответов:

objdump -d filename | sed '/<functionName>:/,/^$/!d'
person fcr    schedule 01.05.2017

Это работает точно так же, как решение gdb (в том, что оно сдвигает смещения к нулю), за исключением того, что оно не тормозит (выполняет работу примерно за 5 мс на моем ПК, тогда как решение gdb занимает около 150 мс):

objdump_func:

#!/bin/sh
# $1 -- function name; rest -- object files
fn=$1; shift 1
exec objdump -d "$@" | 
awk " /^[[:xdigit:]].*<$fn>/,/^\$/ { print \$0 }" |
awk -F: -F' '  'NR==1 {  offset=strtonum("0x"$1); print $0; } 
                NR!=1 {  split($0,a,":"); rhs=a[2]; n=strtonum("0x"$1); $1=sprintf("%x", n-offset); printf "%4s:%s\n", $1,rhs }'
person PSkocik    schedule 07.08.2016
comment
Я не могу проверить прямо сейчас, но я с нетерпением жду, когда я доберусь до этого. Не могли бы вы немного рассказать об аспекте «смещения смещения к нулю»? Я не видел этого явного в ответах gdb здесь, и я хотел бы услышать немного больше о том, что на самом деле там происходит и почему. - person MvG; 07.08.2016
comment
В основном это выглядит так, как будто функция, на которую вы нацелены (что и делает первый awk), была единственной функцией в объектном файле, то есть, даже если функция начинается, скажем, 0x2d, второй awk сдвинет ее в сторону 0x00 (вычитая 0x2d из адреса каждой инструкции), что полезно, потому что ассемблерный код часто делает ссылки относительно начала функции, и если функция начинается с 0, вам не нужно делать вычитания в уме. Код awk мог бы быть лучше, но, по крайней мере, он выполняет свою работу и довольно эффективен. - person PSkocik; 07.08.2016
comment
Оглядываясь назад, кажется, что компиляция с -ffunction-sections — это более простой способ убедиться, что каждая функция начинается с 0. - person PSkocik; 21.01.2020

Завершение Bash для ./dasm

Полные имена символов для этого решения (версия на языке D):

  • Набрав dasm test и затем нажав TabTab, вы получите список всех функций.
  • Если ввести dasm test m и затем нажать TabTab, будут показаны все функции, начинающиеся с m, или, если существует только одна функция, она будет выполнена автоматически. .

Файл /etc/bash_completion.d/dasm:

# bash completion for dasm
_dasm()
{
    local cur=${COMP_WORDS[COMP_CWORD]}

    if [[ $COMP_CWORD -eq 1 ]] ; then
    # files
    COMPREPLY=( $( command ls *.o -F 2>/dev/null | grep "^$cur" ) )

    elif [[ $COMP_CWORD -eq 2 ]] ; then
    # functions
    OBJFILE=${COMP_WORDS[COMP_CWORD-1]}

    COMPREPLY=( $( command nm --demangle=dlang $OBJFILE | grep " W " | cut -d " " -f 3 | tr "()" "  " | grep "$cur" ) )

    else
    COMPREPLY=($(compgen -W "" -- "$cur"));
    fi
}

complete -F _dasm dasm
person Vitaly Fadeev    schedule 08.05.2018

может это легко сделать:
objdump -d libxxx.so | grep -A 50 func_name_to_be_searched

person galian    schedule 19.01.2021
comment
Я написал в вопросе, что objdump -d не показывал эту функцию, предположительно из-за того, что она статическая. - person MvG; 19.01.2021
comment
Извините, я не знаю, почему. - person galian; 19.01.2021