Assembly

スタック・関数・システムコール

値を一時保存し、処理を関数へ分け、OSへ仕事を依頼する方法を学びます。

x86-64 Linux・NASM記法を対象にしています。

Assembly入門の講座一覧

01

スタックを使う

スタックは後入れ先出しの領域です。pushで積み、popで最後に積んだ値を取り出します。

積んだ順番と逆に取り出すASSEMBLY
mov rax, 10
mov rbx, 20
push rax
push rbx
pop rcx       ; rcx = 20
pop rdx       ; rdx = 10
02

callとretで関数に分ける

callは戻り先をスタックへ積んでラベルへ移動し、retは戻ります。retの前にスタックを壊さないことが重要です。

2倍する関数(complete.asm)ASSEMBLY
section .text
    global _start

_start:
    mov rdi, 6
    call double_value
    mov rdi, rax
    mov rax, 60
    syscall

double_value:
    mov rax, rdi
    add rax, rax
    ret
実行結果OUTPUT
echo $?
12

callの直後に関数本体を置く場合も、関数から戻った後に終了処理へ進む必要があります。

03

Linuxへ依頼する

CPU命令だけでは端末表示はできません。x86-64 Linuxではraxへシステムコール番号を置き、rdi以降へ引数を置いてsyscallを実行します。

処理番号主な引数
write1fd、アドレス、長さ
exit60終了コード
環境依存に注意 番号と引数の規則はOS・CPU・ABIに依存します。
04

表示処理を関数にする

writeを呼ぶ処理を関数へ分けると、メイン処理の流れを読みやすくできます。なおsyscallはrcxとr11を壊すため、必要な値は退避します。

print_messageASSEMBLY
section .data
    message db "Assembly function", 10
    message_length equ $ - message
section .text
    global _start
_start:
    call print_message
    mov rax, 60
    xor rdi, rdi
    syscall
print_message:
    mov rax, 1
    mov rdi, 1
    mov rsi, message
    mov rdx, message_length
    syscall
    ret
実行結果OUTPUT
Assembly function