@uents blog

Code wins arguments.

プログラムはなぜ動くのか - ニーモニックをまとめてみる

最近アセンブリ言語の章を読む際に、コードを書きながらアセンブリを出力させて遊んでいるのだが、

  • テキストは、Windows & Boland C++
  • 手元の環境は、ELF形式 (Ubuntu & GCC (Ver. 4.12))
と、異なる環境のせいか、ニーモニックにかなりの違いがある。

オペコード/オペランドの種類

まずは、オペコード/オペランドの種類を表にまとめてみた。

オペコード
オペランド
機能
push
A
Aの値をスタックに格納
pop
A
スタックから値を取りだしAに格納
mov
A B
Aの値をBに格納
sub
A B
Bの値からAの値を引いてBに格納
lea
A B
Aのアドレスが指す領域の値をBに格納??
and
A B
Bの値をAでアラインしてBに格納
call
A
Aという手続きを呼び出す
leave

これ何??
ret

手続きの読みだし元に処理を戻す




ちなみにmovの場合だと、movb、movw、movlなど後ろに1文字付くものが出てくる。これは取り扱う変数の桁に応じて変わるみたいで、
  • 32bit (int型/long型) の場合、movl
  • 16bit (short型) の場合、movw
  • 8bit (char型) の場合、movb
となるみたい。ただし、レジスタレジスタへの値のコピーの場合は、普通に"mov"となる。
以下のような、ソースコードを用意
void func( void )
{
char a;
short b;
int c;

a = 1;
b = 2;
c = 3;
}


コンパイルしてobjdumpしてみる。
$ gcc -g -c -O0 show-opcode.c -o show-opcode
$ objdump -S show-opcode > show-opcode.asm

gccのオプションで"-g"、objdumpのオプションで"-S"を付けると、アセンブラコードにソースコードを混ぜたものを出力してくれる。

アセンブラのコードを見ると、

show-opcode:     file format elf32-i386

Disassembly of section .text:

00000000 :

void func( void )
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
char a;
short b;
int c;

a = 1;
6: c6 45 ff 01 movb $0x1,0xffffffff(%ebp)
b = 2;
a: 66 c7 45 fc 02 00 movw $0x2,0xfffffffc(%ebp)
c = 3;
10: c7 45 f8 03 00 00 00 movl $0x3,0xfffffff8(%ebp)
}
17: c9 leave
18: c3 ret


movb、movw、movlと分けて使われていることがわかる。

レジスタの種類

これもプロセッサによって異なるらしいが、以下の種類のレジスタがあるみたい。

レジスタ
名前
主な機能
eax
アキュムレータ
演算に使う
ebx
ベース・レジスタ
メモリアドレスを格納
ecx
カウント・レジスタ
ループ回数をカウント
ebp
ベースポインタ・レジスタ
データを格納する領域のメモリアドレスを格納
esp
スタックポインタ・レジスタ
スタック領域のメモリアドレス(=スタックポインタ)を格納




レジスタ名の頭に"e"が付くのは、元々はレジスタのサイズは16bitでありレジスタ名もax、bx、…だったらしいが、そのレジスタを32bitに拡張した時の名残で"e"が付いたらしい。

アセンブリのコード例

以上を踏まえて、いくつか例を上げてみる。
pop %ebp
スタック領域から値を取りだし、ベースポインタ・レジスタに格納する
ちなみに、レジスタの前には"%"プリフィックスを付けるのがルールらしい。

mov %esp, %ebp
スタックポインタ・レジスタの値を、ベースポインタ・レジスタに格納する
すなわち、現時点でのスタックポインタを他のレジスタに退避させている。

mov -8(%ebp), %eax
"(%レジスタ)"と括弧が付く場合は、「レジスタが格納しているアドレスのメモリ」と解釈される。
さらに括弧の前の値はオフセットを表していて、"オフセット(%レジスタ)"の場合は「レジスタが格納しているアドレス+オフセットのメモリ」と解釈させる。
よって、ebpに格納しているアドレス+オフセットのメモリの値をアキュムレータに格納する、ということになる。

図にしてみた

文章で書いていても分かりにくいので、OOoで図に書いてみた。こんな感じで合っているだろうか。

ちなみに、間違えてpopとすべきところをpopl、movとすべき所をmovlとしてしまってます…  そこは読み替えて下さい (--; → 直しました。

続きはまた今度。