objdumpを使用して1つの関数を分解する方法は?


91

システムにバイナリがインストールされているので、特定の関数の逆アセンブルを確認したいと思います。できればを使用objdumpしますが、他のソリューションも使用できます。

この質問から、境界アドレスしかわからない場合は、コードの一部を逆アセンブルできる可能性があることがわかりました。この回答から、分割デバッグシンボルを単一のファイルに戻す方法を学びました。

しかし、その単一のファイルを操作し、すべてのコードを逆アセンブルしたとしても(つまり、開始アドレスまたは停止アドレスはありませんが、-dパラメーターは単純ですobjdump)、そのシンボルはどこにも表示されません。問題の関数が静的である限り、これは理にかなっているので、エクスポートされません。それでも、valgrind関数名は報告されるので、どこかに保存する必要があります。

デバッグセクションの詳細を見ると、その名前が.debug_strセクションに記載されていることがわかりますが、これをアドレス範囲に変換できるツールがわかりません。


2
マイナーな補足:関数がマークされている場合static、コンパイラによってその呼び出しサイトにインライン化される可能性があります。これは、それ自体を分解する機能が実際にはない可能性があることを意味する場合があります。他の関数のシンボルを見つけることができるが、探している関数が見つからない場合、これは関数がインライン化されていることを強く示唆しています。ELFファイルのデバッグ情報には、命令が別の場所に移動された場合でも、個々の命令の発信元が格納されるため、Valgrindは元の事前インライン関数を参照する場合があります。
davidg 2014

@davidg:本当ですが、トムの答えがこの場合はうまくいったので、そうではないようです。それでも、たとえば、各命令がどこから来たのかという情報でアセンブリコードに注釈を付ける方法を知っていますか?
MvG 2014

1
聞いてよかった!addr2linePC / IPを受け入れstdin、対応するソースコード行を印刷します。同様に、objdump -lobjdumpをソース行と混合します。ただし、インライン化が多い高度に最適化されたコードの場合、どちらのプログラムの結果も必ずしも特に役立つとは限りません。
davidg 2014

回答:


87

最も簡単なアプローチとしてgdbを使用することをお勧めします。次のように、ワンライナーとして実行することもできます。

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

4
文書化されていない機能を+1します!-ex 'command'入ってないman gdb!?しかし、実際にはgdbdocsにリストされています。また、他の人にとっては、のようなもの/bin/lsが削除される可能性があるので、その正確なコマンドが何も表示しない場合は、別のオブジェクトを試してください!ファイル/オブジェクトをベアワード引数として指定することもできます。例:gdb -batch -ex 'disassemble main' /bin/ls
hoc_age 2014年

3
マニュアルページは決定的なものではありません。長い間、実際には維持されていませんでしたが、今ではメインのドキュメントから生成されていると思います。また、「gdb--help」もより完全になりました。
トムトロメイ2014年

7
gdb /bin/ls -batch -ex 'disassemble main'同様に機能します
stefanct 2016

1
を使用column -ts$'\t'してGDB出力をフィルタリングすると、生のバイトとソース列が適切に配置されます。また、-ex 'set disassembly-flavor intel'-exのsの前にIntelアセンブリ構文が生成されます。
ルスラン

disassemble fn上記の方法を使用して呼び出しました。しかし、バイナリファイルに同じ名前の関数が複数ある場合、1つだけが逆アセンブルされるようです。それらすべてを逆アセンブルすることは可能ですか、それとも生のアドレスに基づいて逆アセンブルする必要がありますか?
TheAhmad

28

disassemble/rsソースバイトとrawバイトも表示するgdb

このフォーマットでは、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、GDB7.11.1でテスト済み。

objdump + awkの回避策

/unix/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that-has-theで言及されているように段落を印刷します-テキスト

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

例えば:

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'

適応元:awk / sedで複数回発生する可能性のある2つのマーカーパターン間の線を選択する方法

メーリングリストの返信

メーリングリストには不可能だという2010年のスレッドがあります:https//sourceware.org/ml/binutils/2010-04/msg00445.html

gdbTomによって提案された回避策に加えて、-ffunction-sectionセクションごとに1つの関数を配置し、セクションをダンプするコンパイルの別の(より悪い)回避策についてもコメントしています。

Nicolas Cliftonは、WONTFIXhttps //sourceware.org/ml/binutils/2015-07/msg00004.htmlを提供しました。これはおそらくGDBの回避策がそのユースケースをカバーしているためです。


gdbアプローチは、共有ライブラリとオブジェクトファイルで正常に機能します。
トムトロメイ2015

16

Objdumpを使用して単一の関数を分解する

私には2つの解決策があります:

1.コマンドラインベース

この方法は完全に機能し、さらに単純な方法です。-dフラグを指定してobjdumpを使用し、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(レコード区切り文字)を改行の2倍に変更すると、$ 1フィールド内で検索するだけなので、推奨される関数を簡単に検索できます。

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

もちろん、mainを印刷したい他の関数に置き換えることができます。

2.Bashスクリプト

この問題のために小さな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を使用する方法に加えて、ライブラリはメモリにロードされないため、より安全です。


Vitaly Fadeevは、このスクリプトのオートコンプリートをプログラムしました。これは本当に素晴らしい機能であり、入力を高速化します。

スクリプトはここにあります


それが速いかどうobjdumpかによるようgdbです。巨大なバイナリ(Firefoxのlibxul.so)objdumpは永遠にかかるので、1時間gdbもかからずに、1時間後にキャンセルしました。
サイモン

6

ごく最近のbinutils(2.32 +)がある場合、これは非常に簡単です。

--disassemble=SYMBOLobjdumpに渡すと、指定された関数のみが逆アセンブルされます。開始アドレスと終了アドレスを渡す必要はありません。

LLVM objdumpにも同様のオプション(--disassemble-symbols)があります。


ありがとうございました。binutils 2.32の変更ログ、2019年2月2日:lists.gnu.org/archive/html/info-gnu/2019-02/msg00000.html " Objdumpの--disassembleオプションは、逆アセンブリの開始シンボルを指定するパラメーターを受け取ることができるようになりました。逆アセンブリ次のシンボルや関数の最後に、このシンボルのアップから続けます。
osgx

5

他の回答と比較してobjdumpの出力を解析するためのawkの使用法を単純化するには:

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

4

これは、ラグがないことを除いて(オフセットをゼロに向かってシフトするという点で)、gdbソリューションと同じように機能します(gdbソリューションは約150msかかるのに対し、PCでは約5msでジョブを完了します)。

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 }'

今はテストできませんが、これに取り掛かるのを楽しみにしています。「オフセットをゼロにシフトする」という側面について少し詳しく説明していただけますか?ここでのgdbの回答にはこれが明示されていませんでした。実際にそこで何が起こっているのか、そしてその理由についてもう少し聞きたいと思います。
MvG 2016

基本的に、ターゲットとする関数(最初awkに実行する関数)がオブジェクトファイル内の唯一の関数であるかのように見えます。つまり、関数がたとえば0x2dで開始された場合でも、2番目のawkはそれを0x00(減算することによって)にシフトします。0x2d各命令のアドレスから)。これは、アセンブリコードが関数の開始を基準にして参照することが多く、関数が0から始まる場合は、頭の中で減算を行う必要がないため便利です。awkコードの方が優れている可能性がありますが、少なくともそれは機能し、かなり効率的です。
PSkocik 2016

振り返ってみると、コンパイルする方-ffunction-sectionsが、各関数が0から始まることを確認する簡単な方法のようです。
PSkocik20年

3

のBash完了 ./dasm

完全なシンボル名、このソリューション(D LANG版):

  • 入力してdasm testからを押すとTabTab、すべての機能のリストが表示されます。
  • 入力してdasm test mからmでTabTab 始まるすべての関数を押すと表示されます。関数が1つしかない場合は、オートコンプリートされます。

ファイル/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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.