GCCでC/C++ソースからアセンブリ出力を取得する方法
はじめに
この記事では、gcc(GNU Compiler Collection)を使用して、CまたはC++のソースコードからアセンブリ出力を生成する方法を解説します。
-Sオプションによるアセンブリ出力の取得
gccには、コンパイル過程における中間出力をすべて取得できる便利な機能が備わっています。アセンブリ出力を得るには、「-S」オプションを指定します。このオプションを使うと、コンパイルまでは実行されますが、アセンブラに渡される直前の段階で処理が止まり、その時点のアセンブリコードが出力されます。
基本構文
gcc -S program.cpp
デフォルトでは、拡張子「.s」が付いたファイル(上記の例では program.s)にアセンブリコードが出力されます。「-o」オプションを併用すれば、出力ファイル名を自由に指定することも可能です。
gcc -S program.cpp -o output.s
サンプルプログラム
それでは、実際の出力結果を確認してみましょう。ここでは、次のようなシンプルなC++プログラムを使用します。2つの数値を変数xとyに格納し、その合計を別の変数sumに代入した後、結果を標準出力に表示するだけのプログラムです。
ソースコード
#include <iostream>
using namespace std;
main() {
int x, y, sum;
x = 50;
y = 60;
sum = x + y;
cout << "Sum is: " << sum << endl;
}
アセンブリ出力の例
上記のプログラムに対して「gcc -S test_cpp.cpp」を実行すると、以下のようなアセンブリコードが生成されます。
.file "test_cpp.cpp"
.text
.section .rodata
.type _ZStL19piecewise_construct, @object
.size _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
.zero 1
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.LC0:
.string "Sum is: "
.text
.globl main
.type main, @function
main:
.LFB1493:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $50, -12(%rbp)
movl $60, -8(%rbp)
movl -12(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
movl %eax, -4(%rbp)
leaq .LC0(%rip), %rsi
leaq _ZSt4cout(%rip), %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movl -4(%rbp), %eax
movl %eax, %esi
movq %rdx, %rdi
call _ZNSolsEi@PLT
movq %rax, %rdx
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZNSolsEPFRSoS_E@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1493:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB1982:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L5
cmpl $65535, -8(%rbp)
jne .L5
leaq _ZStL8__ioinit(%rip), %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rdx
leaq _ZStL8__ioinit(%rip), %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L5:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1982:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1983:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1983:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits
知っておくと便利な関連オプション
- -masm=intel:デフォルトのAT&T構文ではなく、Intel構文でアセンブリコードを出力できます。
- -O0 / -O1 / -O2 / -O3:最適化レベルを指定します。最適化レベルによって生成されるアセンブリコードは大きく変わるため、比較してみると興味深い学習になります。
- -fverbose-asm:アセンブリコードに変数名などのコメントを付与して出力するため、コードの対応関係を理解しやすくなります。
まとめ
gccの「-S」オプションを使えば、たった1つのコマンドでC/C++のソースコードからアセンブリコードを簡単に取得できます。コンパイラがどのように高水準言語を機械語レベルの命令へ変換しているのかを知りたい場合や、パフォーマンスチューニングを行いたい場合に非常に役立つテクニックなので、ぜひ活用してみてください。
-
LinuxにC++コンパイラをインストールする方法(GCC・Clangの手順を解説)
Linux上でC++プログラムをコンパイルするには、いくつかの選択肢があります。本記事では、代表的な2つのコンパイラ「GCC」と「Clang」の確認方法およびインストール手順について解説します。 GCCの確認とインストール ほとんどのLinuxディストリビューションには、標準でGCC(GNU Compiler Collection)がインストールされています。まずは、お使いのシステムにGCCが導入されているかどうかを確認しましょう。ターミナル(コマンドライン)から以下のコマンドを実行してください。 $ g++ -v GCCがすでにインストールされている場合は、次のようなバージョン情報が表示され
-
PHPでパスからファイル名を取得する方法をわかりやすく解説
PHPでファイルパスからファイル名を取得するには、主に「pathinfo()」関数と「basename()」関数の2つの方法があります。それぞれの使い方を、実際のコード例と実行結果とともに解説します。 方法1:pathinfo()関数を使う pathinfo()関数は、パスに関する情報を連想配列として返す関数です。「basename」キーを指定することで、拡張子を含むファイル名を簡単に取得できます。 コード例 <?php $file = pathinfo(/home/cg/root/6985034/main.php); echo $file[basename], \n;