Aliase: Unterschied zwischen den Versionen

Aus C und Assembler mit Raspberry
KKeine Bearbeitungszusammenfassung
KKeine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
== Aliase ==
== Was sind Aliase? ==
== Laden von Registern ==
-> mov x0,x1; Ein Alias. Gleiche ist möglich mit add x0, xzr, x1, aber tatsächlich ist es orr x0,xzr,x1


Sourcecode:
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.
  mov x1,#100
  mov x xzr, x1
  mov x1,#300
  orr x0,xzr,x1


0000000000000000 <_start>:
Beispiel:
  0: d2800c81 mov x1, #0x64                  // #100
  4: aa0103e0 mov x0, x1
  8: d2801901 mov x1, #0xc8                  // #200
  c: 8b0103e0 add x0, xzr, x1
  10: d2802581 mov x1, #0x12c                // #300
  14: aa0103e0 mov x0, x1
  18: d2800000 mov x0, #0x0                  // #0
  1c: d2800ba8 mov x8, #0x5d                  // #93
  20: d4000001 svc #0x0
0,x1
  mov x1,#200
  add x0,


-> Verwendung von Aliase, um den Code besser zu verstehen, Problem beim debuggen, objdump, da eventuell andere Aliases verwendet werden.
```asm
mov x0, x1
```


Die von dir beobachtete Diskrepanz resultiert aus dem Unterschied zwischen der **Assembly-Syntax** und der tatsächlichen **Maschinencodierung** sowie der Art und Weise, wie `objdump` und der Assembler die Befehle interpretieren. Lass uns das Schritt für Schritt erklären:
Dieser Befehl existiert **nicht als eigener Maschinenbefehl**, sondern ist ein **Alias** für:
 
```asm
orr x0, x1, xzr
```
 
Der Befehl `orr` (bitweises ODER) mit dem Wert `xzr` (Zero-Register) ergibt effektiv `mov`.


---
---


### **1. ARM64-Befehlsaliasing**
== Warum macht man das? ==
In ARM64 gibt es viele **Aliase**, d. h. alternative Schreibweisen für denselben Maschinenbefehl. Diese Aliase werden oft verwendet, um die Lesbarkeit des Codes zu verbessern. Beispiele:


- **`MOV x0, x1`** ist ein Alias für **`ORR x0, xzr, x1`**.
- **Lesbarkeit**: `mov x0, x1` ist leichter verständlich als `orr x0, x1, xzr`.
  - Semantik: Kopiere den Inhalt von `x1` nach `x0`.
- **Portabilität**: Viele Architekturen haben einen `mov`, und Entwickler erwarten diesen.
  - Implementierung: Ein logisches OR mit `xzr` (dem Null-Register) ergibt einfach den Wert von `x1`.
- **Compilerfreundlich**: Auch Compiler wie `clang` oder `gcc` generieren „Alias-ähnlichen“ Code.


- **`ADD x0, xzr, x1`** ist ebenfalls ein Alias für **`ORR x0, xzr, x1`**. 
---
  - Semantik: Addiere `x1` zu `xzr` (immer `0`), was effektiv einfach `x1` ist.
 
== Was macht `objdump` daraus? ==
 
Beim Disassemblieren mit `objdump` oder `gdb` siehst du **immer den echten Maschinenbefehl**, nicht den Alias.
 
```asm
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!


---
---


### **2. Warum wird aus `MOV` oder `ADD` ein `ORR`?**
== Was wird wirklich generiert? ==
Die ARM64-Maschinenarchitektur verwendet für mehrere Befehle denselben Opcode. Der tatsächlich verwendete Maschinenbefehl für alle drei (`MOV`, `ADD xzr`, `ORR`) lautet **`ORR`**, da dieser Opcode universell genug ist, um die gewünschten Operationen auszuführen.
 
Nehmen wir `mov x0, x1`:
 
- Quelltext (Alias):
  ```asm
  mov x0, x1
  ```
- Assembler-Expansion:
  ```asm
  orr x0, x1, xzr
  ```
- Maschinencode (Hex):
  ```text
  0xaa0103e0
  ```


Der Assembler interpretiert den Code basierend auf der Absicht des Programmierers:
➡️ Ob du `mov x0, x1` oder `orr x0, x1, xzr` schreibst, ergibt **denselben Maschinencode**.
- **`MOV`**: Ein Alias, wird zu einem `ORR` kompiliert.
- **`ADD xzr`**: Ein Alias für denselben `ORR`.
- **`ORR`**: Der explizite Maschinenbefehl.


---
---


### **3. Warum zeigt `objdump` wieder `MOV` oder `ADD` an?**
== Bekannte Aliase im ARM64-Assembler ==
Das Tool `objdump` arbeitet nicht nur auf Maschinencode-Ebene, sondern versucht, den Assembly-Code durch **Rückübersetzung** lesbar zu machen. Dabei:
- Entscheidet es sich für die **verständlichste Darstellung**, basierend auf dem Kontext und den Aliases.
- Wenn es sieht, dass der Maschinenbefehl einem Alias entspricht (z. B. `ORR x0, xzr, x1`), gibt es ihn möglicherweise als `MOV x0, x1` oder `ADD x0, xzr, x1` zurück.


In deinem Fall:
| Alias          | Entsprechender Instruktionscode                | Beschreibung                            |
1. Der Assembler hat den Alias `MOV x0, x1` in den Maschinenbefehl `ORR x0, xzr, x1` kompiliert.
|----------------|------------------------------------------------|------------------------------------------|
2. `objdump` hat diesen Maschinenbefehl rückübersetzt und **den Alias gewählt, der am besten passt**.
| `mov x0, x1`   | `orr x0, x1, xzr`                              | Kopiere Register                        |
  - Hier hat es sich wieder für `MOV x0, x1` oder `ADD x0, xzr, x1` entschieden, obwohl der zugrunde liegende Befehl tatsächlich `ORR` ist.
| `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    |


---
---


### **4. Warum nicht immer `ORR`?**
== Warum ist das wichtig? ==
Tools wie `objdump` bevorzugen eine lesbare Darstellung:
 
- **`MOV`** ist für Menschen intuitiver als `ORR xzr`.
1. **Disassembler zeigen keine Aliase** → verwirrend bei Analyse.
- Der Assembler und Disassembler abstrahieren unnötige Details, um den Code einfacher zu machen.
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.


---
---
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` ==
```asm
    .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 ===
```bash
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` ===
```asm
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:


### **Zusammenfassung**
```bash
Dein Verständnis ist korrekt: Alle drei Befehle (`MOV`, `ADD xzr`, `ORR`) sind **funktional identisch**. `objdump` wählt jedoch je nach Kontext einen Alias aus, der die Intention des Originalcodes besser widerspiegelt.
objdump -d -M no-aliases alias_test
```


Es gibt also keinen Fehler in deiner Beobachtung. Wenn du immer den zugrunde liegenden Maschinenbefehl sehen möchtest, kannst du die Binärdatei mit einem Hex-Editor oder Disassembler auf Opcode-Ebene analysieren. Dort siehst du, dass alle drei als `ORR` codiert sind.
Das zeigt dir, wie es **der CPU intern** wirklich vorliegt.

Version vom 10. April 2025, 17:17 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:

```asm mov x0, x1 ```

Dieser Befehl existiert **nicht als eigener Maschinenbefehl**, sondern ist ein **Alias** für:

```asm 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.

```asm 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):

 ```asm
 mov x0, x1
 ```

- Assembler-Expansion:

 ```asm
 orr x0, x1, xzr
 ```

- Maschinencode (Hex):

 ```text
 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.

--- 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`

```asm

   .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

```bash 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`

```asm 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:

```bash objdump -d -M no-aliases alias_test ```

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