Programmieren mit ARM64 Assembler
Der ARM ist ein sogenannter RISC-Computer, was das Erlernen von Assembler theoretisch einfacher macht.
- Wir verwenden Linux
- Grundzustand von Rasbpian ist ausreichend
- Zahlen
-> Dezimal, Binär, Hexadezimal
- 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:
| 31 | 30 | 29 | 28-24 | 23-22 | 21 | 20-16 | 15-10 | 9-5 | 4-0 | | Bits | Opcode | Set Condition Code | Opcode | Shift | 0 | Rm | imm6 | Rn | Rd |
-> Erklärung
- Memory
-> Instruktionen 32-Bit, Register 64-Bit, Memory-Adresssierung 64-Bit -> wie lösen.
- Der GCC-Assembler
-> Maschinencode in lesbare Form -> Aufbau von Befehlen, Beispiel ldr, mov
- Erstes Programm: Hello World
.global _start _start:
mov x0,#1 ldr x1,=helloworld mov x2,#13 mov x8,#64 svc 0 mov x0,#0 mov x8,#93 svc 0
.data helloworld:
.ascii "Hello World!\n"
as -o HelloWorld.o HelloWorld.s
ld -o HelloWorld HelloWorld.o
- Erklärung Code
-> Kommentare -> globales Symbol _start -> Assembler-Befehle: --> mov, ldr, svc 0 -> data
- Linux-Systemaufrufe
-> Parameter in den Registern X0–X7 -> Rückgabe x0 -> Funktionsnummer in X8
- Diassemblieren
-> objdump -s -d HellowWorld.o
Laden und Addieren
- mov add
- negative Zahlen
-> Beispiel: 5 + -3
3 in 1 byte is 0x03 or 0000 0011. Inverting the bits is 1111 1100 Add 1 to get 1111 1101 = 0xFD Now add 5 + 0xFD = 0x102 = 2
- Big vs. Little Endian
-> Reihenfolge der Bytes im Speicher -> ARM-Prozessor erlaubt beide Versionen -> Linux verwendet Little Endian
- Shiften und Rotation
- Carry-Flag
• Logical shift left • Logical shift right • Arithmetic shift right • Rotate right
- 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 -> Verwendung von Aliase, um den Code besser zu verstehen, Problem beim debuggen, objdump, da eventuell andere Aliases verwendet werden.
- mov
-> 1. MOVK XD, #imm16{, LSL #shift}
2. MOV XD, #imm16{, LSL #shift}
3. MOV XD, XS
4. MOV XD, operand2
5. MOVN XD, operand2
- add/adc
1. ADD{S} Xd, Xs, Operand2
2. ADC{S} Xd, Xs, Operand2
Beispiel:
.global _start _start: MOVN W0, #2 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 label_else //If ist okay b label_EndIf
label_else:
//else kommt zum zuge
label_EndIf:
- Logische Operatoren
and{s} Xd, Xs, Operand2 eor{s} Xd, Xs, Operand2 orr{s} Xd, Xs, Operand2 bic{s} Xd, Xs, Operand2
| and | eor | orr | bic | Xs | 1100 | 1100 | 1100 | 1100 | Operand2 | 1010 | 1010 | 1010 | 1010 | Ergebnis | 1000 | 0110 | 1110 | 0100 |
- CMN Xn, Operand2
-> addiert Operand2 von Xn
- TST Xn, Operand2
-> Bitweise AND von Operand2 und Xn
Speicher
- Speicheradressen 64 Bit lang
- .data
-> Dezimalzahl: Beginnt mit 1-9 und enthält 0-9 -> Oktalzahl: Beginnt mit 0 und enthält 0-7 -> Binärzahl: Beginnt mit 0b und enthält 0-1 -> Hexadezimal: Beginnt mit 0x und enthält 0-f -> Gleitkommawerte: Beginnt mit 0f oder 0e und enthält die Gleitkomma-Zahl -> Präfix: "-" nimmt das Zweierkomplement, "~" nimmt das Einerkompliment
.byte -0xa3, -22, ~0b11010010
Directive Beschreibung .ascii Eine Zeichenfolge in doppelten Anführungszeichen .asciz Eine mit 0 Bytes abgeschlossene ASCII-Zeichenfolge .byte 1-byte Ganzzahl .double Gleitkommawerte mit doppelter Genauigkeit .float Gleitkommawerte .octa 16-byte Ganzzahl .quad 8-byte Ganzzahl .short 2-byte Ganzzahl .word 4-byte Ganzzahl
- Hilfreiche Direktiven:
.fill Anzahl, Größe, Inhalt: Erzeugt einen Speicher mit dem "Inhalt" der "Größe" und "Anzahl". .rept Anzahl ... .endr: Wiederholt "Anzahl" den Inhalt von "..."
- ASCII-Strings
Escape Description \b Backspace (ASCII code 8) \f Seitenvorschub (ASCII code 12) \n Neue Zeile (ASCII code 10) \r Return (ASCII code 13) \t Tabulator (ASCII code 9) \ddd Ein Okctaler ASCII code (ex \123) \xdd Ein Hexadezimaler ASCII code (ex \x4F) \\ Das “\” Zeichen \” Das Anführungszeichen
- Daten ausrichten
.align Ausrichtung: Setzt den nächsten Wert auf ein "Ausrichtung" definierten Speicher. Beispiel:
.byte 0xef .align 4 //Ausrichtung auf Wortbreite .word 0x26ef43de
- ldr
- Relative Adressierung am PC
-> ldr PC-relativ Adresse 19-Bit breit (ca. +/-1MB) Beispiel:
ldr x1,=hello ... .data hello: .ascii "Hallo"
Die Adresse zum Label "hello" wird relativ zum PC ermittelt. -> TRICK:
ldr x1,=0x1234567890abcdef //Direkte 64-Bit Zuweisung
Der Assembler wandelt diese Anweisung:
ldr x1,#8 .quad 0x1234567890abcdef
- ldr{Typ} xt,[xa], wobei für Typ steht:
B Unsigned byte SB Signed byte H Unsigned halfword (16 bits) SH Signed halfword (16 bits) SW Signed word
Beispiel:
ldr x0,=MeineZahl //läd die Adresse meiner Variablen ldr x0,[x0] //läd dann den Inhalt der Variablen .data MeineZahl: .quad 0x1234567fe
- str{Typ} xt,[xa]
- Indexsierung (Arrays)
PseudoCode:
dim a[10] as word a[5] = 10 //setze das 5 Element mit 10 x = a[8] //Schreibe das 8 Element nach x for i = 1 to 10 a[i] = i *8 next
In Assembler:
ldr x0,=MeinArray //Lade die Adresse von MeinArray nach x0 mov w1,#10 //Speichere 10 in w1 ab str w1,[x0,#(4*4)] //Speichere w1 in das 5 Element (Begin ist 0) (Word = 4 Byte) ldr w1,[x0,#(7*4)] //lade das 8 Element nach w1 mov w2,#1 //i=1 forloop: lsl w4,w2,#3 // i * 8 str w4,[x0, w2, lsl #2] // speichere der Wert aus w4 nach Basisadresse x0 mit index aus w2, der um 4 Multipliziert wird. add w2,w2,#1 // i=i+1 cmp w2,#10 // if i<10 b.le forloop
- Der Index kann auch negativ sein.
- Post-indexierte Adressierung
-> ldr x1,[x2], #4 //Holt den Wert aus x2 und erhöht anschließen x2 um 4
- Interaktion mit anderen Programmiersprachen
- Zugriff auf Hardwaregeräte
- Anweisungen für den Gleitkommaprozessor
- Anweisungen für den NEON-Prozessor