← SEARCHABLE ARCHIVE
Assembly · 2026-08-15

x86-64-Register und -Befehle richtig lesen

Von Teilregistern, Operandengrößen, MOV und LEA bis zur Maschinencode-Codierung

INTERACTIVE READING MODE →

x86-64-Register und -Befehle richtig lesen

Diese Blogartikel sind als Lehrmaterial gedacht. Der tatsächliche Code hängt von CPU-Generation, Betriebssystem, ABI, Assembler, Compiler und Build-Optionen ab. Prüfen Sie deshalb immer die Zielumgebung.

rax, eax, ax und al wirken zunächst wie vier verschiedene Register. Entscheidend ist jedoch nicht, jede Mnemonik auswendig zu lernen, sondern Breite, Quelle, Ziel und jede Änderung des Maschinenzustands zu verfolgen.

Drei Ebenen auseinanderhalten

C-Ausdruck          total += values[i];
Assemblerdarstellung add rax, qword ptr [rcx + rdx*8]
Maschinencode        48 03 04 D1

C beschreibt Typen und Absicht, Assemblersprache stellt Befehle und Operanden lesbar dar, und Maschinencode ist die Bytefolge für den Prozessor. Eine Quellcodezeile entspricht nicht zwingend genau einem Befehl.

Allgemeine und partielle Register

x86-64 besitzt 16 allgemeine 64-Bit-Register: rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp sowie r8 bis r15. Einige Rollen sind architektonisch, etwa die Verwendung von rsp durch Stackbefehle. Argumentregister bestimmt dagegen das ABI.

NameZugegriffener Teil
raxalle 64 Bit
eaxuntere 32 Bit
axuntere 16 Bit
aluntere 8 Bit
ahBit 8–15, historischer High-Byte-Name

Ein Schreibzugriff auf ein 32-Bit-Register löscht im 64-Bit-Modus die oberen 32 Bit.

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

8- und 16-Bit-Schreibzugriffe lassen die übrigen Bits unverändert.

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

Darum löscht ein Compiler oft zuerst eax, bevor er setcc al verwendet. ah, bh, ch und dh lassen sich nicht in Befehlen codieren, die ein REX-Präfix benötigen; neuer Code sollte sie meist vermeiden.

Operandenbreite ist Teil der Bedeutung

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

Die Adresse ist gleich, aber die Zugriffsbreite unterscheidet sich. Eine falsche Breite kann Nachbarfelder überschreiben, eine Seitengrenze überqueren oder einen Layoutvertrag verletzen.

mov kopiert

Ein normales mov löscht die Quelle nicht. Die meisten allgemeinen Formen können auch nicht direkt von Speicher zu Speicher kopieren.

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

Das ist kein universelles Verbot für Befehle mit mehreren Speicherwirkungen. String- und Read-modify-write-Befehle haben eigene Formen; maßgeblich ist die Befehlsreferenz.

Erweiterung zeigt die Ganzzahlinterpretation

Das Byte 0xFF kann unsigned 255 oder signed -1 bedeuten.

movzx eax, byte ptr [rcx]   ; 255
movsx eax, byte ptr [rcx]   ; Vorzeichenerweiterung auf -1
movsxd rax, eax             ; signed 32 auf 64 Bit

movzx füllt neue hohe Bits mit Null, movsx mit dem Vorzeichenbit. Da Typnamen im Disassembly fehlen, sind Erweiterung, Vergleich und Verzweigung gemeinsam nur Indizien für signedness.

lea liest keinen Speicher

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

rax erhält die effektive Adresse; der dort gespeicherte Wert wird nicht gelesen.

lea rax, [rcx + 8]          ; rax = rcx + 8
mov rax, [rcx + 8]          ; lädt den 8-Byte-Wert
lea eax, [rcx + rcx*2]      ; eax = rcx * 3

Compiler nutzen lea auch für Addition und konstante Multiplikation, ohne arithmetische Flags zu verändern.

Arithmetik überschreibt das Ziel

add rax, rdx
sub rcx, 8
and eax, 0FFh
xor edx, edx       ; damit ist ganz rdx Null

In Intel-Syntax gilt meist destination = destination op source. Diese Befehle ändern zusätzlich die Bedingungsflags. Bei einem folgenden jcc, setcc oder cmovcc muss die tatsächlich flagsetzende Anweisung gefunden werden.

Intel- und AT&T-Syntax

; Intel: Ziel zuerst
mov rax, qword ptr [rbx + 8]

# AT&T: Quelle zuerst
movq 8(%rbx), %rax

Der CPU-Befehl ist derselbe, aber eine verwechslte Operandenreihenfolge kehrt den Datenfluss um.

Variable Befehlslänge

x86-64-Befehle sind 1 bis 15 Byte lang und können Präfix, Opcode, ModR/M, SIB, Displacement und Immediate enthalten.

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]

Die Bytes müssen nicht auswendig gelernt werden. Ihre Struktur ist aber wichtig, wenn Befehlsgrenzen, Patches, Relocations oder ein um ein Byte verschobener Disassembler geprüft werden.

Kleine Funktion vollständig verfolgen

Unter dem System-V-ABI kann int transform(int x) { return x * 3 + 5; } so aussehen:

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

Das Argument steht in edi, das Ergebnis in eax. Hardware-Wraparound definiert jedoch nicht automatisch signed Overflow in C; dieser ist undefiniertes Verhalten.

Lesekontrolle

  1. Intel- oder AT&T-Syntax?
  2. Welche Operandenbreite?
  3. Was wird gelesen und überschrieben?
  4. Löscht ein 32-Bit-Schreibzugriff die obere Hälfte?
  5. Wie wird die effektive Adresse berechnet?
  6. Werden Flags erzeugt oder verbraucht?
  7. Gibt das ABI eine Argument-, Ergebnis- oder Sicherungsrolle vor?

Zusammenfassung

x86-64 wird als Zustandsänderung gelesen. Teilregister überlappen, ein 32-Bit-Schreibzugriff löscht die obere Hälfte, mov kopiert, Erweiterungsbefehle zeigen die Ganzzahlinterpretation, und lea berechnet eine effektive Adresse ohne Speicherzugriff.

Offizielle Quellen