ARM64 (AArch64) und ARM32 (Thumb-2) schreiben, bauen und debuggen – ohne Visual Studio.
Mit .arch arm64 erzeugt Asmonite eine Windows-Datei für ARM64 (AArch64, PE32+), mit .arch thumb eine für 32-Bit-ARM (ARM32, Thumb-2, PE32). Der Assembler schreibt DOS-Kopf, PE-Kopf, Abschnitte und die Import-Tabelle selbst; ein Programm ist eine einzige Textdatei. Zum Prüfen liest Dismonite das Ergebnis zurück, und der Debugger läuft auf einem echten Windows-on-ARM-Rechner.
Die Beispiele sind im Ordner examples\windows_arm des ZIPs. Zum Bauen läuft Asmonite auf einem gewöhnlichen Windows-Rechner; ausgeführt wird das Ergebnis auf einem Windows-on-ARM-Rechner.
Ein Programm für Windows on ARM64: printf aus der C-Laufzeit schreibt eine Zeile, exit beendet. Datei: examples\windows_arm\hello_arm64.asm.
.format pe console .arch arm64 .import msvcrt.dll printf, exit .code start: stp x29, x30, [sp, #-16]! adrp x0, msg add x0, x0, :lo12:msg ; first argument in x0 adrp x16, printf ldr x16, [x16, :lo12:printf] blr x16 mov x0, #0 adrp x16, exit ldr x16, [x16, :lo12:exit] blr x16 .data msg db 'Hello, World!', 10, 0
start:
stp x29, x30, [sp, #-0x10]!
adrp x0, section_data
add x0, x0, #0
adrp x16, import_table
ldr x16, [x16, #0x40]
blr x16
mov x0, #0
adrp x16, import_table
ldr x16, [x16, #0x48]
blr x16
Das Programm ist nicht an eine feste Adresse gebunden: Windows legt ein ARM64-Programm an eine beliebige Stelle. Deshalb gibt es keine absoluten Adressen. adrp lädt die Seite (4 KB) eines Labels relativ zum pc, :lo12: ist der Rest innerhalb der Seite. Dasselbe Paar holt die Adresse einer importierten Funktion aus der Import-Tabelle (ldr x16, [x16, :lo12:printf]) und blr x16 ruft sie auf. Das erste Argument steht in x0, die weiteren in x1 bis x7; das Ergebnis kommt in x0 zurück. Rechts siehst du, wie Dismonite dieselbe Datei wieder liest: die Namen der Imports sind zu Positionen in import_table geworden, und Dismonite kann daraus wieder eine .asm machen, die dieselben Bytes ergibt.
Windows on ARM führt 32-Bit-Programme als Thumb-2 aus (PE32, Maschine ARMNT). Dasselbe Programm in 32 Bit. Datei: examples\windows_arm\hello_arm32.asm.
.format pe console .arch thumb .import msvcrt.dll printf, exit .code start: push {r4, lr} adr r0, msg ; first argument in r0 ldr r3, printf ; the address of printf blx r3 movs r0, #0 ldr r3, exit blx r3 .data msg db 'Hello, World!', 10, 0
start:
push {r4, lr}
adr r0, msg
ldr.w r3, [pc, #0x50]
blx r3
movs r0, #0
ldr.w r3, [pc, #0x4c]
blx r3
Hier gibt es keine Seitenpaare: adr und ldr mit einem Label rechnen vom pc aus und reichen nur 4 KB weit. Deshalb liegen Code, Daten und Imports in einem Abschnitt, und der Assembler legt die Import-Adresse so, dass sie in Reichweite ist. Die Argumente stehen in r0 bis r3, der Rest auf dem Stack. Der Zusatz .w im Listing von Dismonite kennzeichnet die lange Thumb-2-Form eines Befehls (4 Byte).
Mit .format pe gui entsteht ein Fensterprogramm. Dieses öffnet ein Meldungsfenster mit MessageBoxA (examples\windows_arm\messagebox_arm64.asm).
.format pe gui .arch arm64 .import user32.dll MessageBoxA .import kernel32.dll ExitProcess .code start: stp x29, x30, [sp, #-16]! mov x0, #0 ; no owner window adrp x1, text add x1, x1, :lo12:text adrp x2, caption add x2, x2, :lo12:caption mov w3, #0 ; type: MB_OK adrp x16, MessageBoxA ldr x16, [x16, :lo12:MessageBoxA] blr x16 mov x0, #0 adrp x16, ExitProcess ldr x16, [x16, :lo12:ExitProcess] blr x16 .data text db 'Hello from a message box!', 0 caption db 'messagebox_arm64', 0
Die vier Argumente von MessageBoxA stehen in x0 bis x3 (Besitzer, Text, Titel, Art). Das Fenster öffnet sich auf dem Desktop, auf dem das Programm läuft: unter dem Debugger auf demselben Rechner, bei einem entfernten Rechner mit Debug-Server auf diesem.
| ARM64 | ARM32 (Thumb-2) | |
|---|---|---|
| Direktive | .arch arm64 | .arch thumb |
| Dateiart | PE32+, Maschine ARM64 | PE32, Maschine ARMNT |
| Argumente | x0 bis x7 | r0 bis r3, dann Stack |
| Adresse eines Labels | adrp + add :lo12: (4-KB-Seiten, beliebig weit) | adr / ldr vom pc (4 KB Reichweite) |
| Import aufrufen | adrp + ldr + blr | ldr r3, name + blx r3 |
| Befehlslänge | immer 4 Byte | 2 oder 4 Byte |
| Debug-Server | asmodbg64 | asmodbg |
Im PE-Kopf jeder Windows-Datei steht, für welchen Prozessor sie ist. Für ARM sind es mehr als zwei Arten; so ordnen Asmonite und Dismonite sie ein.
| Kennung | Art | Läuft auf | Asmonite | Dismonite |
|---|---|---|---|---|
0xAA64 | ARM64 (AArch64), PE32+ | Windows 10 und 11 auf ARM64 | assemblieren, debuggen (.arch arm64) | zerlegen, debuggen |
0x01C4 | ARM32 mit Thumb-2 (ARMNT), PE32 | Windows RT (8, 8.1) und Windows 10 auf ARM; Windows 11 auf ARM64 führt keine 32-Bit-ARM-Programme mehr aus | assemblieren, debuggen (.arch thumb) | zerlegen, debuggen |
0x01C0, 0x01C2 | das alte ARM (ARM-Modus und Thumb von vor Thumb-2) | Windows CE, Pocket PC, Windows Mobile, Embedded Compact; auf keinem Windows-PC | nein: kein Assembler dafür | nur 0x01C0: zerlegt (der Export schreibt den Code als Bytes mit dem Befehl als Kommentar), kein Debuggen |
0xA641, 0xA64E | ARM64EC und ARM64X (Windows 11) | Windows 11 auf ARM64 | nein | nein |
Zum Vergleich: Auf einem Windows-10-Rechner mit ARM64 (rund 47.000 PE-Dateien) kamen ARM64, ARM32 mit Thumb-2, x86 und x64 vor. Die x86-Dateien enthalten zum Teil hybriden Code (ein x86-Kopf, aber ARM64-Befehle, den Windows für die Emulation mitliefert) und reine Sprachdateien (.mui). Das alte ARM fand sich dort nur in selbst mitgebrachten Dateien.
examples\windows_arm in Asmonite öffnen und F9 drücken, oder in der Eingabeaufforderung asmoc hello_arm64.asm. Das Ergebnis ist eine .exe, die jeder Windows-on-ARM-Rechner ausführt.asmodbg (32 Bit) und asmodbg64 (ARM64) liegen im ZIP im Ordner debugserver. Sie laufen dort, wo das Programm laufen soll. Verbunden wird über ssh oder direkt im Netz mit einem Kopplungscode; auf einem Windows-on-ARM-Rechner selbst geht es auch lokal..exe, zeigt den Code mit Labels und Kommentaren und exportiert eine .asm, die Asmonite wieder in dieselbe Datei übersetzt.Die Befehlsinfo (F1) kennt die ARM- und ARM64-Befehle: Syntax, erlaubte Operanden, Flags und ein Beispiel.