Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'

Ответ 2

GDB disassemble/rs чтобы показать исходные и необработанные байты

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

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

переменный ток:

#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 -std=c99 -O0 -g a.c
gdb -batch -ex 'file a.out' -ex "disassemble/rs myfunc"

Разборка:

Dump of assembler code for function main:
a.c:
1       int main(void) {
   0x00000000004004d6 <+0>:     55      push   %rbp
   0x00000000004004d7 <+1>:     48 89 e5        mov    %rsp,%rbp

2           int i;
3           i = 0;
   0x00000000004004da <+4>:     c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)

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

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

6           return 0;
   0x00000000004004e8 <+18>:    b8 00 00 00 00  mov    $0x0,%eax

7       }
   0x00000000004004ed <+23>:    5d      pop    %rbp
   0x00000000004004ee <+24>:    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-that-has-the -текст

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

например:

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

дает просто:

000000000000064a <myfunc>:
 64a:   55                      push   %rbp
 64b:   48 89 e5                mov    %rsp,%rbp
 64e:   89 7d fc                mov    %edi,-0x4(%rbp)
 651:   83 45 fc 02             addl   $0x2,-0x4(%rbp)
 655:   d1 65 fc                shll   -0x4(%rbp)
 658:   8b 45 fc                mov    -0x4(%rbp),%eax
 65b:   5d                      pop    %rbp
 65c:   c3                      retq 

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

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

адаптировано из: Как выбрать линии между двумя шаблонами маркеров, которые могут встречаться несколько раз с помощью 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 охватывает этот случай использования.

Ответ 3

Разобрать одну функцию с помощью 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

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

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

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

2. Bash Script

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

#!/bin/bash
# Author: abu
# 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 не будет загружать библиотеки в память и поэтому безопаснее!


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

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

Ответ 4

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

objdump_func:

#!/bin/sh
# $1 -- function name; rest -- object files
fn=$1; shift 1
exec objdump -d "[email protected]" | 
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 }'

Ответ 5

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

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

Ответ 6

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

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

  • Набрав dasm test и затем нажав Tab Tab, вы получите список всех функций.
  • Набрав dasm test m и затем нажав Tab Tab, будут показаны все функции, начинающиеся с 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