アセンブリ言語とは

📝機械語を人間にわかりやすい形で記述する、代表的な低水準言語である.

一般的には最適化コンパイラが人手で書かれたアセンブリ言語のコードと同等の性能を発揮すると言われている.

メモリ

Memory Layout

2^n-1
|---------------------|
| Stacks              | ローカル変数, プロシージャの内容
|                     |
| Dynamic Data (Heap) | new や malloc で獲得できる領域
| Static Data         | グローバル変数など.
| Literals            | 文字列
| Instructions        | プログラム. 関数
|---------------------|
0

ISA

命令セットアーキテクチャ. (Instruction Set Architecture)

コンピュータのハードウェアに対して命令を伝えるための言葉の語彙.

x86, x64 (x86-64) ISA

インテルや AMD の ISA. 32bit が x86, 64bit が x64.

x86, x86-64 ISA の文法

レジスタ

load / store

  • Load data from memory into %reg = Mem[address]
  • Store register data into memory Mem[address] = %reg

メモリからメモリへのデータ移動は一回の操作ではできない. Cannot do memory-­-memory transfer with a single instruction.

レジスタの種類

  • x86 registers

    caller-Save%eax
    caller-Save%edx
    caller-Save%ecx
    callee-Save%ebx
    callee-Save%esi
    callee-Save%edi
    Special%esp
    Special%ebp
  • x64 registers

    raxreturn value
    rbxCaller Saved
    rcxArgument#4
    rdxArgument#3
    rsiArgument#2
    rdiArgument#1
    rspStack Pointer
    rbpCallee saved
    r8Argument#5
    r9Argument#6
    r10Caller Saved
    r11Caller Saved
    r12Callee Saved
    r13Callee Saved
    r14Callee Saved
    r15Callee Saved

汎用レジスタ

  • %eax,%edx, %ecx

    Caller saves prior to call if values are used later. 呼び出し元が引数を渡すために利用.

  • %eax

    サブルーチンからの戻り値を受けとるために利用することがおおい.

  • %ebx, %esi, %edi

    Callee saves if wants to use them.

ベースレジスタ %rsb, %esp

スタックレジスタ %rsp, &esp

スタックポインタはサブルーチンコールの戻りアドレスをメモリに自動的に格納したり, PUSH, POP 命令でレジスタを一時的に退避, 復帰する場合に使われます.

プログラムカウンタ %rip, %eip

次に実行する命令のアドレス

命令

mov

mov 命令は src オペランドを dest オペランドにコピーします.

mov src, dest

push, pop

スタックを操作するための命令. スタックとは, ebp と esp ではさまれたメモリのこと.

|---| %ebp
|	 |
|	 |
|	 |
|---| %esp
  • push スタックに値を入れる
    • Fetch value from Src
    • Decrement%esp by 4
    • Store value at address given by %esp
  • pop スタックから値を取り出す
    • Load value from address %esp
    • Write value to Dest
    • Increment %esp by 4
  • leave 以下の二つの instruction との糖衣構文
    • mov %ebp, %esp
    • pop %ebp

call, ret

関数に飛ぶ.

400e79:	e8 bf 03 00 00       	callq  40123d <strings_not_equal>

ret でサブルーチンを抜ける

add, sub

  • 加減算
  • add eax, ecx => eax = eax + ec

lea

Address Computa?on Instruction . アドレス計算命令. lea 命令は, src オペランドのアドレスを計算し, そのアドレスを dest オペランドにロードします.

lea src, dest

jmp

  • jmp 無条件ジャンプ
  • jc, jnc CF が立っているかどうか
  • jz, jnz ZF が立っているかどうか
  • js, jns SF が立っているかどうか
  • jo, jno OF が立っているかどうか

test

  • 論理積
  • test eax, eax
  • eax=0 なら ZF=1 となるので , jz 命令などで分岐

cmp

  • 比較
  • cmp eax, 0
  • eax=0 なら ZF=1 となるので , jz 命令などで分岐

xor

  • 排他的論理和
  • xor eax, eax
  • 同じ値同士なら 0 になるので, test や cmp の準備に多用される.

x86-64 Calling Conventions (呼出規約)

プログラムで関数を呼び出す際に, レジスタやスタックを使いどのように引数を渡すか, 戻り値をどのように受け取るかは呼出規約 (calling convention) で決められている

the x86-64 ISA passes the first six arguments to a function in registers. Registers are used in the following order:

rdi, rsi, rdx, rcx, r8, r9.

The return value for functions is passed in rax.

サブルーチンプロローグ

_function:
    push ebp       ;ベースポインタを保存
    mov ebp, esp   ;現在のスタックフレームを指すようベースポインタを変更
    sub esp, x     ;局所変数 (C でいう自動変数) の大きさの分スタックポインタを減らす

サブルーチンエピローグ

mov esp, ebp   ;局所変数を除去
pop ebp        ;ベースポインタを復帰
ret            ;サブルーチンから戻る

制御構文

if

すべて, 「二者を比べ」て「結果によってジャンプ」.

int absdiff (int x, int y)
{
  int result;
  if (x > y) {
    result = x-y;
  } else {
    result = y-x;
  }
  return result;
}
 
int goto_ad ( int x, int y)
{
  int result;
  if (x <= y)
    goto Else ;
  result = x-y;
Exit :
  return result;
Else: result = y-x;
  goto Exit ;
}
int x %edx
int y %eax
 
absdiff :
        pushl %ebp
        movl %esp , %ebp
        ; body
        movl 8 (%ebp), %edx
        movl 12 (%ebp), %eax
        cmpl %eax , %edx ;
        jle .L7
        ; body end
        subl %eax , %edx ; y-x
        movl %edx , %eax
.L8:
        leave ret
.L7:
        subl %edx , %eax ; x-y
        jmp .L8

while

while ( sum != 0 ) {
  <loop body>
}
loopTop: cmpl $0, %eax
    je loopDone
         <loop body code>
    jmp  loopTop
loopDone:

for

for は while の糖衣構文.

switch

Jump Table (indirect jmp) を利用する.

[jump table のアドレス + 8 * (引数)] で求められるアドレスの値 (switch 先のアドレスが格納されている) を参照して, そのアドレスにジャンプする.2 回ジャンプする. それが Indirect という意味.

  • Direct: jmp .L61
    • Jump target is denoted by label .L61
  • Indirect: jmp *.L62 (,%edx,4)
    • Start of jump table: . L62
    • Must scale by factor of 4 (labels are 32-bits = 4 bytes on IA32)
    • Fetch target from effective address .L62 + edx*4

Tools

gdb

デバッカ.

コンパイル時に-g g -O0 オブションをつける必要がある.

$ gcc -g g -O0 ソースコード名
オプション意味
-gファイルにデバッグ情報を付加する.
これがないとデバッグ時に変数名や行番号が表示されない
-O0最適化を行わない. 最適化を行うと,
コードの入れ替えや削除が行われてしまい, デバッグしにくくなる

ステップ実行

  • run (r)

    プログラムの実行.

  • step (s)

    プログラムをステップ実行. ステップオーバー.

  • stepi

    アセンブリコードのステップ実行.

  • next (n)

    プログラムをステップ実行. ステップイン.

  • cont (c)

    プログラムの再開.

ブレークポイント操作

  • breakpoint (b)

    ブレークポイントをはる.

    (gdb) b 行番号
    (gdb) b 関数名
    (gdb) b ファイル名:行番号
  • info b

    ブレークポイントの情報を表示.

  • delete (d)

    ブレークポイントのクリア.

メモリ情報

  • disas

    アセンブリコードの表示

  • info register (info reg)

    レジスタの情報を表示

  • x

    メモリの状態表示, x/[表示する個数][出力書式][出力単位]

    出力単位意味
    b1 バイト
    h2 バイト
    w4 バイト
    g8 バイト
    (gdb) x/6xw

    ステップカウンタの 3 ステップ先までを表示

    (gdb) x /3i $rip

    カレントスタックポインタからの情報を表示

    (gdb) x /30x $rsp
    (gdb) x /30xg $rsp

    アドレスの値を文字で表示

    (gdb) x /s $eax
  • info locals (i lo)

    ローカル変数表示.

その他

  • バックトレース (bt)

    バックトレースとは, ユーザ・プログラムが現在いる箇所にどのようにして到達したかを示す要約情報

    (gdb) bt
    #0  0x000000000040174e in read_six_numbers ()
    #1  0x0000000000400eac in phase_2 ()
    #2  0x0000000000400dd5 in main (argc=<optimized out>, argv=0x7fffffffdb58) at bom

objdump

  • t ファイルのシンボルテーブルエントリを表示します. nm によって得られる情報とほぼ同じ
  • T ファイルの動的なシンボルテーブルエントリを表示します. 「nm -D」とほぼ同じ
  • S (可能であれば) ソースコードを逆アセンブル結果と混在させて表示します
  • d Use this to disassemble all of the code.

nm

オブジェクトファイルのシンボルをリストする.

Reference