gccでC / C ++ソースからアセンブラ出力を取得するにはどうすればよいですか?


379

どうやってこれを行うのですか?

何かがコンパイルされている方法を分析したい場合、どのようにして出力されたアセンブリコードを取得できますか?


8
asm出力を人間が読めるようにする
Peter Cordes

回答:


422

-Sgcc(またはg ++)のオプションを使用します。

gcc -S helloworld.c

これにより、helloworld.cでプリプロセッサ(cpp)が実行され、初期コンパイルが実行され、アセンブラが実行される前に停止します。

デフォルトでは、これはファイルを出力しますhelloworld.s-oオプションを使用して、出力ファイルを引き続き設定できます。

gcc -S -o my_asm_output.s helloworld.c

もちろん、これは元のソースがある場合にのみ機能します。結果のオブジェクトファイルしかない場合の代替方法はobjdump--disassembleオプションを設定して(または-d省略形で)を使用することです。

objdump -S --disassemble helloworld > helloworld.dump

このオプションは、(-gコンパイル時に)オブジェクトファイルに対してデバッグオプションが有効になっていて、ファイルが削除されていない場合に最適に機能します。

実行file helloworldすると、objdumpを使用して取得する詳細のレベルがわかります。


3
これは正しいですが、Cr McDonoughの回答の結果の方が役立つと思いました。
Rhys Ulerich 2013年

3
さらに、objdump -M intel -S --disassemble helloworld> helloworld.dumpを使用して、Linuxのnasmと互換性のあるIntel構文でオブジェクトダンプを取得します。
touchStone 2015年

2
最適化/チェックする単一の関数がある場合は、インタラクティブなC ++コンパイラ、つまりgodbolt
fiorentinoing

1
@touchStone:GAS .intel_syntaxはNASMと互換性がありません。それはよりMASMに似mov eax, symbolています(たとえばmov r32, imm32、それがアドレスのNASMとは異なり、負荷です)が、MASMと完全に互換性もありません。特に、NASM構文で記述したい場合は、読みやすい形式として強くお勧めします。objdump -drwC -Mintel | lessまたはgcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less便利です。(GCC / clangアセンブリ出力から「ノイズ」を削除する方法も参照してください)。 -masm=intelclangでも動作します。
Peter Cordes

3
使いgcc -O -fverbose-asm -S
やすさ

173

これにより、Cコードと行番号を組み合わせたアセンブリコードが生成され、どの行がどのコードを生成するかをより簡単に確認できます。

# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst

で発見されたプログラマのためのアルゴリズム、3ページ(PDFの全体の15ページです)。


3
(これは実際にはページ(番号付き)3(PDFの15ページ目)にあります))
Grumdrig

1
悲しいことにas、OS Xではこれらのフラグを認識していません。ただし、そうした場合は、-Waオプションをに渡すためにこれを1行で使用できますas
Grumdrig 2013

23
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lstこれの短縮版になります。
legends2k 2013年

4
次のいずれgcc -c -g -Wa,-ahl=test.s test.cかを使用することもできますgcc -c -g -Wa,-a,-ad test.c > test.txt
phuclv 2014年

1
ブログの記事は、これをより詳細に説明する伝説のような1-コマンドのバージョンを含むとLu'uを掲載しました。しかし、なぜ-O0ですか?これは、値の追跡を困難にするロード/ストアでいっぱいであり、最適化されたコードがどれほど効率的であるかについては何もわかりません。
Peter Cordes

51

次のコマンドラインは、Christian Garbinのブログからです。

g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt

暗黙のキャストを含むルーチンに対して、Win-XPのDOSウィンドウからG ++を実行しました

c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'

出力は、元のC ++コードで反復されたアセンブルされた生成コードです(C ++コードは、生成されたasmストリームのコメントとして表示されます)

  16:horton_ex2_05.cpp **** using std::setw;
  17:horton_ex2_05.cpp ****
  18:horton_ex2_05.cpp **** void disp_Time_Line (void);
  19:horton_ex2_05.cpp ****
  20:horton_ex2_05.cpp **** int main(void)
  21:horton_ex2_05.cpp **** {
 164                    %ebp
 165                            subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55                    call ___main
167 0129 89E5          .stabn 68,0,21,LM2-_main
168 012b 81EC8000      LM2:
168      0000
169 0131 E8000000      LBB2:
169      00
170                    .stabn 68,0,25,LM3-_main
171                    LM3:
172                            movl $0,-16(%ebp)

@パラディン-必ずしもそうではありません。OPは、C / C ++ソースコードと同等のアセンブラー出力を取得することでした。これにより、リストが取得されます。これは、コンパイラーとオプティマイザーが何をしているのかを理解するのに役立ちます。しかし、行番号を期待していないため、アセンブラ自体がバーフを引き起こし、アセンブリ命令の左側にバイトをコンパイルします。
ジェシーチザム

-O2gccがコードを最適化する方法を確認したい場合は、少なくとも、またはプロジェクトのビルド時に実際に使用する最適化オプションを使用してください。(または、必要に応じてLTOを使用する場合は、リンカーの出力を逆アセンブルして、実際に何が得られるかを確認する必要があります。)
Peter Cordes

27

-Sスイッチを使用する

g++ -S main.cpp

またはgccでも

gcc -S main.c

参照してくださいこれを


7
FAQを確認してください:「あなた自身のプログラミングの質問をして答えるのも全く問題ありません」。重要なのは、StackOverflowに他の人のためのリソースとしてQ&Aが含まれていることです。
スティーブジェソップ

そして、誰かが一緒に来て、より良い答えであなたを驚かせるかもしれませんが、私のものは時々少し冗長かもしれません...
PhirePhly

あなた自身の質問ボタンに答えることさえあります。
Ciro Santilli郝海东冠状病六四事件法轮功

13

表示したいものが出力のリンクに依存している場合、前述のgcc -Sに加えて、出力オブジェクトファイル/実行可能ファイルのobjdumpも役立ちます。以下は、デフォルトのobjdump構文をより読みやすいnasm構文に変換する、Loren Merrittによる非常に便利なスクリプトです。

#!/usr/bin/perl -w
$ptr='(BYTE|WORD|DWORD|QWORD|XMMWORD) PTR ';
$reg='(?:[er]?(?:[abcd]x|[sd]i|[sb]p)|[abcd][hl]|r1?[0-589][dwb]?|mm[0-7]|xmm1?[0-9])';
open FH, '-|', '/usr/bin/objdump', '-w', '-M', 'intel', @ARGV or die;
$prev = "";
while(<FH>){
    if(/$ptr/o) {
        s/$ptr(\[[^\[\]]+\],$reg)/$2/o or
        s/($reg,)$ptr(\[[^\[\]]+\])/$1$3/o or
        s/$ptr/lc $1/oe;
    }
    if($prev =~ /\t(repz )?ret / and
       $_ =~ /\tnop |\txchg *ax,ax$/) {
       # drop this line
    } else {
       print $prev;
       $prev = $_;
    }
}
print $prev;
close FH;

これはgcc -Sの出力でも使用できると思います。


2
それでも、このスクリプトは、構文を完全に変換しない汚いハックです。例えばmov eax,ds:0x804b794、あまりNASMでない。また、時にはそれだけで有益な情報を取り除き:movzx eax,[edx+0x1]メモリオペランドがあったかどうかを推測するために葉リーダーbyteword
Ruslan

そもそもNASM構文で逆アセンブルするには、Agner Fogをobjconv使用します。出力ファイル= /dev/stdoutでstdoutに逆アセンブルできるので、パイプしlessて表示することができます。も存在しますがndisasm、フラットバイナリのみが逆アセンブルされ、オブジェクトファイル(ELF / PE)については認識されません。
Peter Cordes

9

誰もが指摘したように、-SGCC のオプションを使用してください。また、最適化オプションを追加するかどうか(-O0なしの場合-O2、積極的な最適化の場合)によって結果が(大幅に)異なる場合があることも付け加えておきます。

特にRISCアーキテクチャーでは、コンパイラーはしばしば、最適化を行う際に、ほとんど認識できないほどコードを変換します。結果を見るのは印象的で魅力的です!


9

さて、みんなが言ったように、-Sオプションを使用してください。-save -tempsオプションを使用すると、前処理済みファイル(.i)、アセンブリファイル( .s)、およびオブジェクトファイル(* .o)も取得できます。(-E、-S、および-cを使用してそれぞれを取得します。)


8

前述のように、-Sフラグを確認してください。

また、フラグの「-fdump-tree」ファミリー、特に「-fdump-tree-all」を確認する価値があります。これにより、gccの中間形式のいくつかを確認できます。これらは多くの場合、アセンブラーよりも読みやすく(少なくとも私にとって)、最適化パスの実行方法を確認できます。




8

-save-temps

これはhttps://stackoverflow.com/a/17083009/895245で言及されましたが、さらに例を挙げてみましょう。

このオプションの大きな利点は-S、ビルド自体に大きな影響を与えることなく、任意のビルドスクリプトに非常に簡単に追加できることです。

あなたがするとき:

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

そして現在、通常の出力main.oに加えて、現在の作業ディレクトリには次のファイルも含まれています。

  • main.i ボーナスであり、事前に処理されたファイルが含まれています。

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
  • main.s 必要な生成されたアセンブリが含まれています:

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits

多数のファイルに対して実行する場合は、代わりに使用することを検討してください:

 -save-temps=obj

これにより、中間ファイルが-o現在の作業ディレクトリではなくオブジェクト出力と同じディレクトリに保存されるため、潜在的なベース名の競合が回避されます。

このオプションのもう1つの優れた点は、以下を追加した場合です-v

gcc -save-temps -c -o main.o -v main.c

実際には、下の醜い一時ファイルの代わりに使用されている明示的なファイルが表示される/tmpため、前処理/コンパイル/アセンブリの手順を含む、何が行われているのかを正確に知るのは簡単です。

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

Ubuntu 19.04 amd64、GCC 8.3.0でテスト済み。




3

これらのコマンドの出力

WindowsのCプログラムのアセンブリコードを表示/印刷する手順は次のとおりです

コンソール/ターミナル/コマンドプロンプト:

  1. コードブロックのようなCコードエディターでCプログラムを記述し、拡張子.cで保存します。

  2. コンパイルして実行します。

  3. 正常に実行されたら、gccコンパイラーをインストールしたフォルダーに移動し、

    次のコマンドは、「.c」ファイルの「.s」ファイルを取得します

    C:\ gcc> gcc -S Cファイルの完全パスENTER

    コマンドの例(私の場合と同様)

    C:\ gcc> gcc -SD:\ Aa_C_Certified \ alternate_letters.c

    これは、元の「.c」ファイルの「.s」ファイルを出力します

4。この後、次のコマンドを入力します

C; \ gcc> cpp filename.s ENTER

コマンドの例(私の場合と同様)

C; \ gcc> cpp alternate_letters.s

これにより、Cプログラムのアセンブリ言語コード全体が出力/出力されます。


2

オプションとして「-S」を使用します。端末にアセンブリ出力を表示します。


端末に表示するには、を使用しますgcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less-Sそれ自体が作成しますfoo.s
Peter Cordes

2

最近、私はプログラムの各関数の組み立てを知りたいと思いました
いました。これが私のやり方です。

$ gcc main.c                      // main.c source file
$ gdb a.exe                       // gdb a.out in linux
  (gdb) disass main               // note here main is a function
                                  // similary it can be done for other functions

2

以下は、gccを使用したCのソリューションです。

gcc -S program.c && gcc program.c -o output
  1. ここで、最初の部分はプログラムのアセンブリ出力をProgramと同じファイル名で保存しますが、拡張子が.sに変更されているため、通常のテキストファイルとして開くことができます。

  2. ここでの2番目の部分は、実際の使用のためにプログラムをコンパイルし、指定されたファイル名でプログラムの実行可能ファイルを生成します。

program.cを上記の使用は、あなたのプログラムとの名称である出力生成したい実行可能ファイルの名前です。

ところでそれはStackOverFlowの私の最初の投稿です:-}

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