Programmieren mit ARM64 Assembler: Unterschied zwischen den Versionen

Aus C und Assembler mit Raspberry
 
(121 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
Der ARM ist ein sogenannter RISC-Computer, was das Erlernen von Assembler theoretisch einfacher macht.
<!-- /satyria.de/source/assem/ARM64Assembler -->


* Wir verwenden Linux
== Grundlagen ==
Der ARM-Prozessor ist ein sogenannter RISC-Computer (Reduced Instruction Set Computer). Das Designprinzip von RISC-Prozessoren führt zu einem kleineren und übersichtlicheren Befehlssatz, was das Erlernen von Assembler theoretisch einfacher macht. In dieser Einführung werden wir die grundlegenden Konzepte der ARM64-Bit Assembler Programmierung verständlich und praxisorientiert erklären, sodass sie auch für Anfänger zugänglich ist.


* Zahlen
== Was ist Assembler? ==
-> Dezimal, Binär, Hexadezimal
Assembler ist eine Programmiersprache, die es ermöglicht, den Computer auf sehr niedriger Ebene direkt zu steuern. Anders als Hochsprachen wie C oder Python kommuniziert Assembler direkt mit der Hardware, was eine präzise Kontrolle über den Prozessor und den Speicher ermöglicht. Diese direkte Steuerungsmöglichkeit macht Assembler zu einer mächtigen Sprache für ressourcenintensive oder sehr spezifische Aufgaben.
* CPU-Register
-> Ein 64-Bit-Programm auf einem ARM-Prozessor im Benutzermodus hat Zugriff auf 31 Allzweckregister, einen Programmzähler (PC) und eine Kombination aus Nullregister/Stapelzeiger
* x0-x30
* SP, XZR
* x30, LR
* PC
* w0-w30, wzr: sind x-Register, die die unteren 32-Bit verwenden.
-> Zusätzliche Register Gleitkommaoperationen, Neon-Coprozessor, später


* Aufbau von Data processing instructions:
== Aufbau dieses Tutorials ==
  | 31  | 30    | 29                | 28-24  | 23-22 | 21 | 20-16 | 15-10 | 9-5 | 4-0 |
Dieses Tutorial ist so strukturiert, dass es sich von einem Kapitel zum nächsten Schritt für Schritt selbst erklärt. Zur Demonstration der Beispiele verwende ich einen Raspberry Pi 5, aber die Beispiele sollten ebenso auf den Modellen Raspberry Pi 3 und Raspberry Pi 4 funktionieren, sofern diese mit einem 64-Bit-Linux-System betrieben werden. Zu Beginn werden wir uns ansehen, wie man überhaupt Assemblerprogramme schreibt und diese auf einem ARM64-System ausführt.
  | Bits | Opcode | Set Condition Code | Opcode | Shift | 0  | Rm    | imm6  | Rn  | Rd  |
-> Erklärung


* Memory
== Inhalt ==
-> Instruktionen 32-Bit, Register 64-Bit, Memory-Adresssierung 64-Bit -> wie lösen.
* [[Programmierumgebung unter Linux erstellen und testen]]
 
** [[Programmierumgebung unter Linux erstellen und testen#Einrichtung der Entwicklungsumgebung|Einrichtung der Entwicklungsumgebung]]
* Der GCC-Assembler
** [[Programmierumgebung unter Linux erstellen und testen#Installation der notwendigen Tools wie Assembler, Compiler und Debugger|Installation der notwendigen Tools wie Assembler, Compiler und Debugger]]
-> Maschinencode in lesbare Form
** [[Programmierumgebung unter Linux erstellen und testen#Erste Schritte mit einem einfachen "Hello, World!"-Programm in Assembler|Erste Schritte mit einem einfachen "Hello, World!"-Programm in Assembler]]
-> Aufbau von Befehlen, Beispiel ldr, mov
** [[Programmierumgebung unter Linux erstellen und testen#Testen der Umgebung durch Kompilieren und Ausführen von Assemblerprogrammen|Testen der Umgebung durch Kompilieren und Ausführen von Assemblerprogrammen]]
 
* [[Tools, die zur Programmierung benötigt werden]]
* Erstes Programm: Hello World
** [[Tools, die zur Programmierung benötigt werden#Assembler (as)|Allgemeine Einführung in gängige Tools: Assembler (as), Linker (ld), Compiler (gcc) und make]]
.global _start
** [[Tools, die zur Programmierung benötigt werden#Debugging-Tool (gdb)|Verwendung von Debugging-Tools wie gdb]]
_start: mov X0, #1
** [[Tools, die zur Programmierung benötigt werden#IDEs und Texteditoren, die Assembler unterstützen|IDEs und Texteditoren, die Assembler unterstützen]]
ldr X1, =helloworld
* [[Allgemeines zu Zahlen (Dezimal, Binär, Hexadezimal)]]
mov X2, #13
* [[Das erste Programm "Hello World"]]
mov X8, #64
* [[Register und Speicher]]
svc 0
* [[Laden und Speichern von Werten]]
mov X0, #0
** [[Laden und Speichern von Werten#Laden von konstanten Werten in Register|Laden von konstanten und variablen Werten in Register]]
mov X8, #93
** [[Laden und Speichern von Werten#Speichern von Registerwerten im Speicher|Speichern von Registerwerten im Speicher]]
svc 0
** [[Laden und Speichern von Werten#Laden und Speichern: Die Befehle ldr und str|Verwendung der Befehle ldr und str]]
.data
* [[Addieren und Subtrahieren]]
helloworld: .ascii "Hello World!\n"  
** [[Addieren und Subtrahieren#Einfache arithmetische Operationen: add und sub|Einfache arithmetische Operationen: add, sub]]
 
** [[Addieren und Subtrahieren#Umgang mit Überlauf und Unterlauf|Umgang mit Überlauf und Unterlauf]]
as -o HelloWorld.o HelloWorld.s
** [[Addieren und Subtrahieren#Arbeiten mit mehreren Registern und direkten Werten|Arbeiten mit mehreren Registern und direkten Werten]]
ld -o HelloWorld HelloWorld.o
** [[Addieren und Subtrahieren#Umgang mit Überlauf und Unterlauf bei 128-Bit-Arithmetik in ARM64-Assembler|Umgang mit Überlauf und Unterlauf bei 128-Bit-Arithmetik in ARM64-Assembler]]
 
* [[Multiplizieren, Dividieren und Akkumulation]]
* Erklärung Code
** [[Multiplizieren, Dividieren und Akkumulation|Multiplizieren und Dividieren von Werten: mul, div]]
-> Kommentare
** [[Multiplizieren, Dividieren und Akkumulation#Akkumulation|Erweitere Operationen wie akkumuliertes Multiplizieren: mla, mls]]
-> globales Symbol _start
* [[Shiften und Rotation]]
-> Assembler-Befehle:
* [[Logische Operatoren]]
--> mov, ldr, svc 0
* [[Sprungadressen (Labels)]]
-> data
* [[Programmablauf steuern]]
 
** [[Programmablauf steuern|Bedingte und unbedingte Sprungbefehle: b, bl, cbz, cbnz]]
* Linux-Systemaufrufe
** [[Programmablauf steuern#Schleifen|Schleifen und Verzweigungen]]
-> Parameter in den Registern X0–X7
** [[Programmablauf steuern#Bedingter Sprung|Vergleichsoperationen und bedingte Ausführung]]
-> Rückgabe x0
* [[Funktionen und Stack]]
-> Funktionsnummer in X8
** [[Funktionen und Stack#Funktion aufrufen und Rückkehr|Funktion aufrufen und Rückkehr]]
 
** [[Funktionen und Stack#Funktionsparameter und Rückgabewerte|Funktionsparameter und Rückgabewerte]]
* Diassemblieren
** [[Funktionen und Stack#Verwendung des Stacks für lokale Variablen und Funktionsaufrufe|Verwendung des Stacks für lokale Variablen und Funktionsaufrufe]]
-> objdump -s -d HellowWorld.o
** [[Funktionen und Stack#Regeln für das Aufrufen von Funktionen (AAPCS)|Regeln für das Aufrufen von Funktionen (AAPCS)]]
 
* [[Systemaufrufe]]
== Laden und Addieren ==
** [[Systemaufrufe#Einführung in Linux-Systemaufrufe|Einführung in Linux-Systemaufrufe]]
* mov add
** [[Systemaufrufe#Grundlegende Systemaufrufe|Ausführung von grundlegenden Systemaufrufen wie exit, read, write, open, close]]
* negative Zahlen
** [[Systemaufrufe#Übergabe von Parametern und Entgegennahme von Rückgabewerten|Übergabe von Parametern und Entgegennahme von Rückgabewerten]]
-> Beispiel: 5 + -3
** [[Systemaufrufe#Verweis auf alle verfügbaren Linux-Systemaufrufe|Übersicht der Linux ARM64 Systemaufrufen]]
  3 in 1 byte is 0x03 or 0000 0011.
<!--* [[GPIO Programmierung]]
  Inverting the bits is
** [[GPIO Programmierung#Grundlagen der GPIO-Programmierung|Grundlagen der GPIO-Programmierung]]
  1111 1100
** [[GPIO Programmierung#Direkte Steuerung der GPIO-Pins|Direkte Steuerung der GPIO-Pins]]
  Add 1 to get
** [[GPIO Programmierung#Besondere Register und Konfiguration|Besondere Register und Konfiguration]]-->
  1111 1101 = 0xFD
* [[Interaktion mit C]]
  Now add
** [[Interaktion mit C#C-Funktionen aus Assembler aufrufen|C-Funktionen aus Assembler aufrufen]]
  5 + 0xFD = 0x102 = 2
** [[Interaktion mit C#Assemblerfunktionen in C aufrufen|Assemblerfunktion in C aufrufen]]
* Big vs. Little Endian
** [[Interaktion mit C#Inline-Assembler in C|Inline-Assembler in C]]
-> Reihenfolge der Bytes im Speicher
** [[Interaktion mit C#Bibliotheken verwenden|Bibliotheken verwenden]]
-> ARM-Prozessor erlaubt beide Versionen
* [[Gleitkommaoperationen]]
-> Linux verwendet Little Endian
** [[Gleitkommaoperationen#FPU-Register: Überblick|FPU-Register]]
* Shiften und Rotation
*** [[Gleitkommaoperationen#Registerüberblick|Überblick über FPU (Floating Point Unit) und ihre Register]]
* Carry-Flag
** [[Gleitkommaoperationen#Verwendung der FPU-Register in Funktionen|Verwendung der Register in Funktionen]]
• Logical shift left
** [[Gleitkommaoperationen#Hinweise zur Verwendung von FPU-Registern|Arbeiten mit FPU-Registern]]
• Logical shift right
*** [[Gleitkommaoperationen#Beispiele für die Verwendung|Ausführung von Gleitkommaoperationen: fadd, fsub, fmul, fdiv]]
• Arithmetic shift right
** [[Gleitkommaoperationen#Konvertierung von Gleitkommazahlen|Konvertierung von Gleitkommazahlen]]
• Rotate right
*** [[Gleitkommaoperationen#Konvertierung von Gleitkommazahlen|Umwandlung zwischen Ganzzahlen und Gleitkommazahlen]]
* Laden von Registern
** [[Gleitkommaoperationen#Vergleich von Gleitkommazahlen und bedingte Sprungbefehle|Vergleichen]]
-> mov x0,x1; Ein Alias. Gleiche ist möglich mit add x0, xzr, x1, aber tatsächlich ist es orr x0,xzr,x1
*** [[Gleitkommaoperationen#Vergleich von Gleitkommazahlen und bedingte Sprungbefehle|Vergleich von Gleitkommazahlen und bedingte Sprungbefehle]]
-> Verwendung von Aliase, um den Code besser zu verstehen, Problem beim debuggen, objdump, da eventuell andere Aliases verwendet werden.
* [[NEON Coprozessor]]
* mov
** [[NEON Coprozessor#Verwendung des NEON-Coprozessors im Raspberry Pi|Verwendung des NEON-Coprozessors im Raspberry Pi]]
-> 1. MOVK XD, #imm16{, LSL #shift}
** [[NEON Coprozessor#LANE-Prinzip: Überblick|LANE-Prinzip]]
  2. MOV XD, #imm16{, LSL #shift}
** [[NEON Coprozessor#Anwendung und Kontext im Raspberry Pi|Anwendung und Kontext im Raspberry Pi]]
  3. MOV XD, XS
<br>
  4. MOV XD, operand2
* Anhang
  5. MOVN XD, operand2
** [[Diassemblieren]]
* add/adc
** [[Big vs. Little Endian]]
  1. ADD{S} Xd, Xs, Operand2
** [[Aliase]]
  2. ADC{S} Xd, Xs, Operand2
** [[Direktiven|Assembler Direktiven (as/gcc)]]
 
*** [[Direktiven#4. Daten ausrichten: .align|Daten ausrichten]]
Beispiel:
** [[Makros in Assembler]]
.global _start
** [[Übersicht der Linux ARM64 Systemaufrufen]]
_start: MOVN W0, #2
*** [[Übersicht der Fehlercodes|Übersicht der Fehlercodes von Systemaufrufen]]
  ADD W0, W0, #1
  MOV X8, #93 // Service command code 93
  SVC 0
Rückgabe in w0 -> Kann mit "echo $?" ausgegeben werden.
 
* Add with Carry
-> Übertrag.
-> Beispiel:
  adds x1,x3,x5 //addiert untere 64-Bit
  adc  x0,x2,x4 //addiert obere 64-Bit mit Übertrag von zuvor
 
* SUB/SBC
== Tools ==
* GNU MAKE
-> make -B: erstellt neue Kompilierungen
 
* GDB
break (b) line Set breakpoint at line
run (r) Run the program
step (s) Single step program
continue (c) Continue running the program
quit (q or control-d) Exit gdb
control-c Interrupt the running program
info registers (i r) Print out the registers
info break Print out the breakpoints
delete n Delete breakpoint n
x /Nuf expression Show contents of memory
 
* Gross-Compiling
* Emulation
 
== Programmablauf steuern ==
* Bedingungsloser Sprung
-> b label
* Bedingungsflags
negativ: N gesetzt, wenn Ergebnis negativ ist
zero: z gesetzt wenn Ergebnis null ist
carry: c gesetzt, wenn es einen Überlauf gab. add -> wenn größer als Zahlenbereich (Überlauf), Subtraktion gesetzt, wenn Ergebnis keine Ausleihe. bei Verschieben letzte Bit herausgeschoben.
overflow: o gesetzt bei addition und subtraktion, wenn Ergebnis größer oder gleich 2^31, oder kleiner -2^31 
 
Flags werden im NZCV-Systemregister gespeichert
 
* Verzweigung bei Bedingung
-> b.{condition}
{condition} Flags Meaning
EQ Z set Equal
NE Z clear Not equal
CS or HS C set Higher or same (unsigned >=)
CC or LO C clear Lower (unsigned <)
MI N set Negative
PL N clear Positive or zero
VS V set Overflow
VC V clear No overflow
HI C set and Z clear Higher (unsigned >)
LS C clear and Z set Lower or same (unsigned <=)
GE N and V the same Signed >=
LT N and V differ Signed <
GT Z clear, N and V the same Signed >
LE Z set, N and V differ Signed <=
AL Any Always (same as no suffix)
 
* CMP Xn,Operand2
-> subtrahiert Operand2 von Xn
 
* Schleifen
** FOR NEXT
for i = 1 to 10
  Mache etwas
next i
 
In Assembler:
  mov w2,#1  //i=1
loop:
  //Mache etwas
  add w2,w2,#1 // i=i+1
  cmp w2,#10  // if i<10
  b.le loop    // then goto loop
** WHILE
while x < 10
  Mache etwas
end while
 
In Assembler:
  // w2 zuvor initialisiert -> x
  loop:
    cmp w2,#10
    b.ge loopend
    // Mache etwas
    b loop
  loopend:
 
** if/then/else
if x < 10 then
    If ist okay
else
    else kommt zum zuge
end if
 
In Assembler:
  // wert x in w2
  cmp w2,#10
  b.ge
 
 
 
 
 
 
 
* Interaktion mit anderen Programmiersprachen
* Zugriff auf Hardwaregeräte
* Anweisungen für den Gleitkommaprozessor
* Anweisungen für den NEON-Prozessor

Aktuelle Version vom 11. April 2025, 10:39 Uhr


Grundlagen

Der ARM-Prozessor ist ein sogenannter RISC-Computer (Reduced Instruction Set Computer). Das Designprinzip von RISC-Prozessoren führt zu einem kleineren und übersichtlicheren Befehlssatz, was das Erlernen von Assembler theoretisch einfacher macht. In dieser Einführung werden wir die grundlegenden Konzepte der ARM64-Bit Assembler Programmierung verständlich und praxisorientiert erklären, sodass sie auch für Anfänger zugänglich ist.

Was ist Assembler?

Assembler ist eine Programmiersprache, die es ermöglicht, den Computer auf sehr niedriger Ebene direkt zu steuern. Anders als Hochsprachen wie C oder Python kommuniziert Assembler direkt mit der Hardware, was eine präzise Kontrolle über den Prozessor und den Speicher ermöglicht. Diese direkte Steuerungsmöglichkeit macht Assembler zu einer mächtigen Sprache für ressourcenintensive oder sehr spezifische Aufgaben.

Aufbau dieses Tutorials

Dieses Tutorial ist so strukturiert, dass es sich von einem Kapitel zum nächsten Schritt für Schritt selbst erklärt. Zur Demonstration der Beispiele verwende ich einen Raspberry Pi 5, aber die Beispiele sollten ebenso auf den Modellen Raspberry Pi 3 und Raspberry Pi 4 funktionieren, sofern diese mit einem 64-Bit-Linux-System betrieben werden. Zu Beginn werden wir uns ansehen, wie man überhaupt Assemblerprogramme schreibt und diese auf einem ARM64-System ausführt.

Inhalt