Aliase: Unterschied zwischen den Versionen

Aus C und Assembler mit Raspberry
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 16: Zeile 16:


Der Befehl `orr` (bitweises ODER) mit dem Wert `xzr` (Zero-Register) ergibt effektiv `mov`.
Der Befehl `orr` (bitweises ODER) mit dem Wert `xzr` (Zero-Register) ergibt effektiv `mov`.
---


== Warum macht man das? ==
== Warum macht man das? ==


- **Lesbarkeit**: `mov x0, x1` ist leichter verständlich als `orr x0, x1, xzr`.
* Lesbarkeit: `mov x0, x1` ist leichter verständlich als `orr x0, x1, xzr`.
- **Portabilität**: Viele Architekturen haben einen `mov`, und Entwickler erwarten diesen.
* Portabilität: Viele Architekturen haben einen `mov`, und Entwickler erwarten diesen.
- **Compilerfreundlich**: Auch Compiler wie `clang` oder `gcc` generieren „Alias-ähnlichen“ Code.
* Compilerfreundlich: Auch Compiler wie `clang` oder `gcc` generieren „Alias-ähnlichen“ Code.
 
---


== Was macht `objdump` daraus? ==
== Was macht `objdump` daraus? ==


Beim Disassemblieren mit `objdump` oder `gdb` siehst du **immer den echten Maschinenbefehl**, nicht den Alias.
Beim Disassemblieren mit `objdump` oder `gdb` siehst du "immer den echten Maschinenbefehl", nicht den Alias.


<syntaxhighlight lang="asm">
<syntaxhighlight lang="asm">
mov x0, x1    ; geschrieben im Assembler
mov x0, x1    // geschrieben im Assembler
</syntaxhighlight>
</syntaxhighlight>


Zeile 40: Zeile 36:
</syntaxhighlight>
</syntaxhighlight>


⚠️ Das kann verwirrend sein, wenn du Quellcode und disassemblierten Code vergleichst!
Das kann verwirrend sein, wenn du Quellcode und disassemblierten Code vergleichst!
 
---


== Was wird wirklich generiert? ==
== Was wird wirklich generiert? ==
Zeile 48: Zeile 42:
Nehmen wir `mov x0, x1`:
Nehmen wir `mov x0, x1`:


- Quelltext (Alias):
* Quelltext (Alias):
<syntaxhighlight lang="asm">
: <syntaxhighlight lang="asm">
   mov x0, x1
   mov x0, x1
</syntaxhighlight>
</syntaxhighlight>
- Assembler-Expansion:
* Assembler-Expansion:
<syntaxhighlight lang="asm">
: <syntaxhighlight lang="asm">
   orr x0, x1, xzr
   orr x0, x1, xzr
</syntaxhighlight>
</syntaxhighlight>
- Maschinencode (Hex):
* Maschinencode (Hex):
<syntaxhighlight lang="asm">
: <syntaxhighlight lang="asm">
   0xaa0103e0
   0xaa0103e0
</syntaxhighlight>
</syntaxhighlight>


➡️ Ob du `mov x0, x1` oder `orr x0, x1, xzr` schreibst, ergibt **denselben Maschinencode**.
Ob du `mov x0, x1` oder `orr x0, x1, xzr` schreibst, ergibt "denselben Maschinencode".
 
---


== Bekannte Aliase im ARM64-Assembler ==
== Bekannte Aliase im ARM64-Assembler ==


| Alias           | Entsprechender Instruktionscode               | Beschreibung                             |
{| class="wikitable"
|----------------|------------------------------------------------|------------------------------------------|
|-
| `mov x0, x1`   | `orr x0, x1, xzr`                             | Kopiere Register                         |
Alias !! Entsprechender Instruktionscode !! Beschreibung
| `mov x0, #0`   | `movz x0, #0`                                 | Setze Register auf 0                     |
|-
| `cmp x0, x1`   | `subs xzr, x0, x1`                             | Vergleich über Subtraktion ohne Ergebnis |
| `mov x0, x1` || `orr x0, x1, xzr` || Kopiere Register
| `neg x0, x1`   | `sub x0, xzr, x1`                              | Negation                                 |
|-
| `nop`          | `hint #0`                                    | Kein-Operation (für Alignment etc.)     |
| `mov x0, #0` || `movz x0, #0` || Setze Register auf 0  
| `ret`          | `br x30`                                      | Rücksprung aus Funktion                 |
|-
| `b label`      | `b label` (kein Alias)                        | Unbedingter Sprung                       |
| `cmp x0, x1` || `subs xzr, x0, x1` || Vergleich über Subtraktion ohne Ergebnis
| `bl label`      | `bl label` (kein Alias)                      | Sprung mit Link (Funktionsaufruf)      |
|-
| `movk/movz/movn`| Literalladen in 16-Bit-Chunks                | Register initialisieren mit Immediate   |
| `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? ==
== Warum ist das wichtig? ==


1. **Disassembler zeigen keine Aliase** → verwirrend bei Analyse.
# Disassembler zeigen keine Aliase → verwirrend bei Analyse.
2. **Debugging wird leichter**, wenn man den echten Maschinenbefehl kennt.
# Debugging wird leichter, wenn man den echten Maschinenbefehl kennt.
3. **Assemblerfehler** können entstehen, wenn man glaubt, `mov` sei ein „echter“ Befehl mit eigenen Regeln.
# Assemblerfehler können entstehen, wenn man glaubt, `mov` sei ein „echter“ Befehl mit eigenen Regeln.
 
---
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.
 
---


== Beispiel: `alias_test.s` ==
== 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.


<syntaxhighlight lang="asm">
<syntaxhighlight lang="asm">
Zeile 117: Zeile 114:
     ret
     ret
</syntaxhighlight>
</syntaxhighlight>
---


=== Bauen und disassemblieren ===
=== Bauen und disassemblieren ===
Zeile 127: Zeile 122:
objdump -d alias_test
objdump -d alias_test
</syntaxhighlight>
</syntaxhighlight>
---


=== Beispielausgabe von `objdump -d alias_test` ===
=== Beispielausgabe von `objdump -d alias_test` ===
Zeile 144: Zeile 137:
Du siehst:
Du siehst:


| Quellcode Alias     | Disassembler zeigt     | Bedeutung                                 |
{| class="wikitable"
|----------------------|------------------------|---------------------------------------------|
|-
| `mov x0, x1`        | `orr x0, x1, xzr`      | Kopiere x1 nach x0                         |
Quellcode Alias !! Disassembler zeigt !! Bedeutung
| `mov x2, #0`        | `movz x2, #0x0`        | Setze x2 auf 0                             |
|-
| `cmp x0, x1`        | `subs xzr, x0, x1`    | Vergleiche x0 mit x1                       |
| `mov x0, x1`        || `orr x0, x1, xzr`      || Kopiere x1 nach x0                        
| `neg x3, x4`        | `sub x3, xzr, x4`      | x3 = -x4                                   |
|-
| `nop`                | `hint #0`              | Kein Effekt                                 |
| `mov x2, #0`        || `movz x2, #0x0`        || Setze x2 auf 0                            
| `ret`                | `ret`                  | Rücksprung über x30                         |
|-
| `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 ===
=== Weitere Analyse ===


Wenn du **keine Aliase im Disassembly** möchtest (d. h. nur „echte“ Instruktionen), kannst du `objdump` so verwenden:
Wenn du "keine Aliase im Disassembly" möchtest (d. h. nur „echte“ Instruktionen), kannst du `objdump` so verwenden:


<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
Zeile 163: Zeile 163:
</syntaxhighlight>
</syntaxhighlight>


Das zeigt dir, wie es **der CPU intern** wirklich vorliegt.
Das zeigt dir, wie es "der CPU intern" wirklich vorliegt.

Aktuelle Version vom 11. April 2025, 06:41 Uhr

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?

  1. Disassembler zeigen keine Aliase → verwirrend bei Analyse.
  2. Debugging wird leichter, wenn man den echten Maschinenbefehl kennt.
  3. 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.