19
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

この記事について

アセンブラでプログラムする場合、主に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の値を関数の返り値として用います.

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

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







19
17
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?