Aliase
Was sind Aliase?
Ein "Alias" ist eine alternative Schreibweise für eine Instruktion – sozusagen ein „freundlicher“ Name für komplexere Maschinenbefehle. Aliase machen den Code "lesbarer und intuitiver", verändern aber "nicht" den Maschinencode, der letztlich generiert wird.
Beispiel:
mov x0, x1
Dieser Befehl existiert "nicht als eigener Maschinenbefehl", sondern ist ein "Alias" für:
orr x0, x1, xzr
Der Befehl `orr` (bitweises ODER) mit dem Wert `xzr` (Zero-Register) ergibt effektiv `mov`.
Warum macht man das?
- Lesbarkeit: `mov x0, x1` ist leichter verständlich als `orr x0, x1, xzr`.
- Portabilität: Viele Architekturen haben einen `mov`, und Entwickler erwarten diesen.
- Compilerfreundlich: Auch Compiler wie `clang` oder `gcc` generieren „Alias-ähnlichen“ Code.
Was macht `objdump` daraus?
Beim Disassemblieren mit `objdump` oder `gdb` siehst du "immer den echten Maschinenbefehl", nicht den Alias.
mov x0, x1 // geschrieben im Assembler
Ergibt in `objdump -d`:
orr x0, x1, xzr
Das kann verwirrend sein, wenn du Quellcode und disassemblierten Code vergleichst!
Was wird wirklich generiert?
Nehmen wir `mov x0, x1`:
- Quelltext (Alias):
mov x0, x1
- Assembler-Expansion:
orr x0, x1, xzr
- Maschinencode (Hex):
0xaa0103e0
Ob du `mov x0, x1` oder `orr x0, x1, xzr` schreibst, ergibt "denselben Maschinencode".
Bekannte Aliase im ARM64-Assembler
| Alias | Entsprechender Instruktionscode | Beschreibung |
|---|---|---|
| `mov x0, x1` | `orr x0, x1, xzr` | Kopiere Register |
| `mov x0, #0` | `movz x0, #0` | Setze Register auf 0 |
| `cmp x0, x1` | `subs xzr, x0, x1` | Vergleich über Subtraktion ohne Ergebnis |
| `neg x0, x1` | `sub x0, xzr, x1` | Negation |
| `nop` | `hint #0` | Kein-Operation (für Alignment etc.) |
| `ret` | `br x30` | Rücksprung aus Funktion |
| `b label` | `b label` (kein Alias) | Unbedingter Sprung |
| `bl label` | `bl label` (kein Alias) | Sprung mit Link (Funktionsaufruf) |
| `movk/movz/movn` | Literalladen in 16-Bit-Chunks | Register initialisieren mit Immediate |
Warum ist das wichtig?
- Disassembler zeigen keine Aliase → verwirrend bei Analyse.
- Debugging wird leichter, wenn man den echten Maschinenbefehl kennt.
- Assemblerfehler können entstehen, wenn man glaubt, `mov` sei ein „echter“ Befehl mit eigenen Regeln.
Beispiel: `alias_test.s`
Hier ist ein einfaches Beispielprogramm in ARM64-Assembler mit typischen "Alias-Instruktionen", das du kompilieren und mit `objdump` untersuchen kannst. Danach zeige ich dir, wie die `objdump`-Ausgabe dazu aussieht.
.section .text
.global _start
_start:
// Alias: mov x0, x1 -> orr x0, x1, xzr
mov x0, x1
// Alias: mov x2, #0 -> movz x2, #0
mov x2, #0
// Alias: cmp x0, x1 -> subs xzr, x0, x1
cmp x0, x1
// Alias: neg x3, x4 -> sub x3, xzr, x4
neg x3, x4
// Alias: nop -> hint #0
nop
// Alias: ret -> br x30
ret
Bauen und disassemblieren
as alias_test.s -o alias_test.o
ld alias_test.o -o alias_test
objdump -d alias_test
Beispielausgabe von `objdump -d alias_test`
0000000000401000 <_start>:
401000: aa0103e0 orr x0, x1, xzr
401004: d2800002 movz x2, #0x0
401008: eb01001f subs xzr, x0, x1
40100c: cb040060 sub x3, xzr, x4
401010: d503201f nop
401014: d65f03c0 ret
Du siehst:
| Quellcode Alias | Disassembler zeigt | Bedeutung |
|---|---|---|
| `mov x0, x1` | `orr x0, x1, xzr` | Kopiere x1 nach x0 |
| `mov x2, #0` | `movz x2, #0x0` | Setze x2 auf 0 |
| `cmp x0, x1` | `subs xzr, x0, x1` | Vergleiche x0 mit x1 |
| `neg x3, x4` | `sub x3, xzr, x4` | x3 = -x4 |
| `nop` | `hint #0` | Kein Effekt |
| `ret` | `ret` | Rücksprung über x30 |
Weitere Analyse
Wenn du "keine Aliase im Disassembly" möchtest (d. h. nur „echte“ Instruktionen), kannst du `objdump` so verwenden:
objdump -d -M no-aliases alias_test
Das zeigt dir, wie es "der CPU intern" wirklich vorliegt.