← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

x86-64のレジスタと命令を読む方法

部分レジスタ、データ幅、MOVとLEAから機械語エンコーディングまで

INTERACTIVE READING MODE →

x86-64のレジスタと命令を読む方法

本ブログの記事は講義を目的として作成しています。 実際のコードはCPU世代、OS、ABI、アセンブラ、コンパイラ、ビルド設定によって異なるため、対象環境を必ず確認してください。

rax、eax、ax、alは別々のレジスタに見えるが、実際には同じ保存領域の一部を異なる幅で参照する名前である。重要なのはニーモニックの丸暗記ではなく、どの幅を読み、どこへ書き、どの状態が変化するかを追跡することだ。

ソース、アセンブリ、機械語を分ける

C式               total += values[i];
アセンブリ表記     add rax, qword ptr [rcx + rdx*8]
機械語バイト       48 03 04 D1

Cは型と意図を表し、アセンブリは命令とオペランドを人間向けに表し、機械語はCPUがデコードするバイト列である。ソース1行と命令1個が一対一に対応する保証はない。

汎用レジスタと部分レジスタ

x86-64にはrax、rbx、rcx、rdx、rsi、rdi、rbp、rsp、r8〜r15の16本の64ビット汎用レジスタがある。rspはスタック命令が暗黙に使うが、どのレジスタに関数引数を置くかはABIが決める。

名前参照範囲
rax64ビット全体
eax下位32ビット
ax下位16ビット
al下位8ビット
ah8〜15ビットの旧来の名前

64ビットモードで32ビット汎用レジスタへ書くと、上位32ビットは0になる。

mov rax, 0FFFFFFFFFFFFFFFFh
mov eax, 7
; RAX = 0000000000000007h

8ビット・16ビット書き込みは残りのビットを保持する。

mov rax, 1122334455667788h
mov al, 0AAh
; RAX = 11223344556677AAh

したがってsetcc alの前にxor eax, eaxが現れることがある。ah、bh、ch、dhはREXプレフィックスが必要な命令では使用できないため、新しいコードでは避ける方が単純である。

データ幅は命令の意味の一部

mov al,  byte ptr  [rcx]    ; 1バイト
mov ax,  word ptr  [rcx]    ; 2バイト
mov eax, dword ptr [rcx]    ; 4バイト
mov rax, qword ptr [rcx]    ; 8バイト

同じアドレスでもアクセス幅が違う。幅を誤ると隣のフィールドまで読み書きし、ページ境界や構造体レイアウトの契約を破ることがある。

movと整数拡張

通常のmovはコピーであり、元を消さない。また一般的な形式ではメモリ同士を直接コピーできない。

mov rax, [rsi]
mov [rdi], rax

同じ0xFFでも符号なしなら255、符号付きなら-1である。

movzx eax, byte ptr [rcx]   ; 255
movsx eax, byte ptr [rcx]   ; -1を32ビットへ符号拡張
movsxd rax, eax             ; signed 32ビットを64ビットへ

movzxは上位を0で、movsxは符号ビットで埋める。逆アセンブリにはCの型名が残らないため、拡張命令、比較、分岐を合わせてsignednessを推測する。

leaはメモリを読まない

lea rax, [rcx + rdx*4 + 16]

raxには実効アドレスが入る。そのアドレスのメモリ値は読まない。

lea rax, [rcx + 8]          ; rax = rcx + 8
mov rax, [rcx + 8]          ; その場所の8バイトを読む
lea eax, [rcx + rcx*2]      ; eax = rcx * 3

コンパイラはポインタ計算だけでなく、フラグを変更しない加算や定数倍にもleaを使う。

算術とフラグ

Intel構文の2オペランド演算は通常、左側を上書きする。

add rax, rdx
sub rcx, 8
and eax, 0FFh
xor edx, edx       ; rdx全体が0

これらは条件フラグも更新する。後続のjcc、setcc、cmovccがどの命令のフラグを読むか確認する。

Intel構文とAT&T構文

; Intel: 宛先が先
mov rax, qword ptr [rbx + 8]

# AT&T: 元が先
movq 8(%rbx), %rax

表記が違ってもCPU命令は同じだが、オペランド順序を取り違えるとデータフローが逆になる。

命令長は可変

x86-64命令は1〜15バイトで、prefix、opcode、ModR/M、SIB、displacement、immediateなどから構成される。

48 89 D8                mov rax, rbx
B8 2A 00 00 00          mov eax, 42
48 8B 44 D1 10          mov rax, [rcx + rdx*8 + 16]

バイト暗記は不要だが、命令境界、パッチ、開始位置が1バイトずれた逆アセンブルを調べる際に構造が役立つ。

小さな関数を追跡する

System V ABIでint transform(int x) { return x * 3 + 5; }は次のようになり得る。

transform:
    lea eax, [rdi + rdi*2]
    add eax, 5
    ret

入力はedi、戻り値はeaxである。ただしハードウェアのwraparoundが、Cのsigned overflowまで定義するわけではない。

読み取りチェックリスト

  1. Intel構文かAT&T構文か。
  2. オペランド幅はいくつか。
  3. 何を読み、何を上書きするか。
  4. 32ビット書き込みで上位が0になるか。
  5. 実効アドレスはどう計算されるか。
  6. フラグを生成・消費するか。
  7. ABI上の引数・戻り値・保存の役割があるか。

まとめ

x86-64は状態変化として読む。部分レジスタは重なり、32ビット書き込みは上位を0にする。movはコピー、拡張命令は整数の解釈を示し、leaはメモリを読まず実効アドレスを計算する。

公式資料