この記事について
アセンブラでプログラムする場合、主に2通りの方法があります.アセンブラでプログラム全体を作る方法と、一部の関数だけをアセンブラで書く方法です.前者はアセンブラの可読性の低さを考えると現実的ではないです.というわけで後者が主流になります.一部の関数をアセンブラ、それ以外はCで書くという方法で、保守性と実効速度の高さを両立できます.
必要な道具
この記事ではLinux環境を想定しています.アセンブラは環境に強く依存するので、Linux以外では動きません.Linux環境で以下のソフトを用意してください.
- gcc
- nasm
プログラムの作成
下のCプログラムmain.cと、アセンブラプログラムadd.asmをエディタで書いて保存しましょう.
#include <cstdio>
extern int asm_add(int a, int b);
int main()
{
int a, b;
a = 1;
b = 2;
int ans = asm_add(a, b);
printf("%d\n", ans);
return 0;
}
section .text
global _sm_add
asm_add:
enter 0,0
mov eax, edi
mov ebx, esi
add eax, ebx
leave
ret
main.cをコンパイルしてオブジェクトファイルを生成します.
gcc -c -o main.o main.c
add.asmをコンパイルしてオブジェクトファイルを生成します.
nasm -g -f elf64 -o add.o add.asm
生成されたオブジェクトファイルをリンクします.
gcc -o add -lc -g main.o add.o -o add
これで、実行ファイルaddが得られました.
実行すると
3
が、画面に表示されます.
Cプログラム側解説
Cのソースを、解説していきます.とても、簡単なコードです.まずはじめにexternで関数int add(int a, int b)
が宣言されています.
アセンブリファイルでは、Cで通常行うようにヘッダファイルでオブジェクト間のつながりを処理することができません.そこで、extern付きで宣言し外部リンクを指定します.
他の部分は普通のCプログラムと変わりません.
同様にC++でも、アセンブラで作られた関数を呼び出すことができます.
アセンブラ側解説
短いコードなので一行ずつ解説しています.
section .text
コードの始まりを意味します..text以外に、.data(初期化されたデータの宣言)、.bss(初期化されていないデータの宣言)のセクションがありますが、このコードでは省略しています.
global _asm_add
マーカ_asm_addをグローバル指定にし、他のコードからも参照できるようにしています.
asm_add:
は、マーカです.これより下が関数asm_add
の本体です.
enter 0,0
はスタックフレームの構築を行う命令です.スタックフレームは、メモリ上に展開されるデータの保管先です.関数内で使用される変数や、関数が終わったあとに戻る命令のアドレスを保管しています.
mov eax, edi
mov ebx, esi
は、引数、a,bを汎用レジスタに移しています.Linux環境では、関数の引数はレジスタに保管されて渡されます.これは、メモリへアクセスする頻度を減らし処理を高速化するためです.関数のどの引数のデータがどのレジスタに保管されるかは一対一で決まっています.以下に対応表を示します.
レジスタ | 引数 |
---|---|
edi | 第一引数 |
esi | 第二引数 |
edx | 第三引数 |
ecx | 第四引数 |
r8d | 第五引数 |
r9d | 第六引数 |
第七引数からはスタックを経由してやり取りされます.しかし、実行速度や、可読性を考慮すると、第七引数以上は必要ないでしょう.
add eax, ebx
ebxの内容と、eaxの内容を足しています.Linux環境ではeaxの値を関数の返り値として用います.
ret ```
```leave```は、スタックの開放を行います.```enter```と対になる命令です.``` ret ```で、call命令でスタックにプッシュされた関数呼び出し元のアドレスに戻ります.
# 終わりに
これで、cからアセンブリを呼び出せるようになりました.この方法を取ることでコンパイラが、自動で行うには難しい最適化を手動で行えるようになります.
**Happy Hacking!!**
# 参考文献
* "System V Application Binary Interface AMD64 Architecture Processor Supplement"
http://x86-64.org/documentation/abi.pdf
* th0x4c 備忘録:[GDB] Linux X86-64 の呼出規約(calling Convention)を Gdb で確認する
http://th0x4c.github.io/blog/2013/04/10/gdb-calling-convention/
* Guide to Assembly Language Programming in Linux
http://www.amazon.co.jp/Guide-Assembly-Language-Programming-Linux/dp/0387258973
↓↓コメントがあれば励みになります↓↓