12
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

Linuxでx86アセンブラ(Cとの連携編)

この記事について

アセンブラでプログラムする場合、主に2通りの方法があります.アセンブラでプログラム全体を作る方法と、一部の関数だけをアセンブラで書く方法です.前者はアセンブラの可読性の低さを考えると現実的ではないです.というわけで後者が主流になります.一部の関数をアセンブラ、それ以外はCで書くという方法で、保守性と実効速度の高さを両立できます.

必要な道具

この記事ではLinux環境を想定しています.アセンブラは環境に強く依存するので、Linux以外では動きません.Linux環境で以下のソフトを用意してください.
* gcc
* nasm

プログラムの作成

下のCプログラムmain.cと、アセンブラプログラムadd.asmをエディタで書いて保存しましょう.

main.c
#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;
}
add.asm
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の値を関数の返り値として用います.
leave
ret

leaveは、スタックの開放を行います.enterと対になる命令です.retで、call命令でスタックにプッシュされた関数呼び出し元のアドレスに戻ります.

終わりに

これで、cからアセンブリを呼び出せるようになりました.この方法を取ることでコンパイラが、自動で行うには難しい最適化を手動で行えるようになります.
Happy Hacking!!

参考文献

↓↓コメントがあれば励みになります↓↓

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?