Interaktion mit C
In diesem Kapitel werden wir die Integration von ARM64-Bit-Assembler und C-Programmen beleuchten. Die folgenden Themen werden behandelt:
C-Funktionen aus Assembler aufrufen
Um C-Funktionen aus Assembler heraus aufzurufen, müssen Sie die folgenden Schritte beachten:
- Deklaration der C-Funktion in Assembler
- Aufruf der Funktion
- AAPCS (ARM Architecture Procedure Call Standard) beachten
Beispiel in C:
// myfunctions.c
#include <stdio.h>
void my_function(int x) {
printf("The value is: %d\n", x);
}
Beispiel in Assembler:
.extern my_function // Extern deklarierte Funktion
.global _start
_start:
mov x0, #42 // Parameter für die Funktion (Wert: 42)
bl my_function // Aufruf der C-Funktion
// Exit system call
mov x8, #93 // syscall number for exit
mov x0, #0 // exit code
svc 0
Wichtige Hinweise:
- Die C-Funktion muss mit .extern deklariert werden, um den Linker zu informieren.
- Die Parameter müssen gemäß den AAPCS-Regeln in die entsprechenden Register geladen werden (x0 bis x7).
- Der Rückgabewert der Funktion wird in x0 zurückgegeben.
Assemblerfunktionen in C aufrufen
Wenn Sie Assemblerfunktionen in C aufrufen möchten, müssen Sie sie korrekt definieren und deklarieren, um sicherzustellen, dass der C-Compiler und der Linker sie finden und korrekt aufrufen können.
Beispiel in Assembler:
// myfunctions.s
.global add_numbers
add_numbers:
add x0, x0, x1 // Addiere x0 und x1, Ergebnis in x0
ret // Rückkehr zur aufrufenden Funktion
Beispiel in C:
// main.c
#include <stdio.h>
extern int add_numbers(int a, int b); // Deklaration der Assemblerfunktion
int main() {
int result = add_numbers(5, 7);
printf("Result: %d\n", result);
return 0;
}
Kompilierung und Linken:
gcc -c main.c
as -o myfunctions.o myfunctions.s
gcc -o myprogram main.o myfunctions.o
Wichtige Hinweise:
- Die Assemblerfunktion muss mit .global deklariert werden.
- In der C-Datei muss die Assemblerfunktion als extern deklariert werden.
- Parameter und Rückgabewerte müssen gemäß den AAPCS-Konventionen übergeben werden.
Inline-Assembler in C
Inline-Assembler ermöglicht die Einbettung von Assembler-Code direkt in C-Programme, um bestimmte Leistungsoptimierungen oder spezielle Hardwarezugriffe zu ermöglichen.
Beispiel:
#include <stdio.h>
int add_numbers(int a, int b) {
int result;
__asm__ (
"add %w0, %w1, %w2"
: "=r" (result)
: "r" (a), "r" (b)
);
return result;
}
int main() {
int result = add_numbers(5, 7);
printf("Result: %d\n", result);
return 0;
}
Parameter-Erklärung
- "add %w0, %w1, %w2": Assembler-Befehl. %w0, %w1, %w2 sind Platzhalter für die entsprechenden Register. Die w-Präfixe zeigen an, dass wir 32-Bit-Werte (int) verwenden.
- : "=r" (result): Output-Parameter. Das Ergebnis wird in das C-Variable result geschrieben. =r bedeutet, dass der Compiler ein beliebiges Register für das Ergebnis wählen soll, und es ist ein Output-Operanden.
- : "r" (a), "r" (b): Input-Parameter. Die C-Variablen a und b werden den entsprechenden Platzhaltern zugeordnet. Der Compiler wählt Register, um diese Werte zu speichern.
Wichtige Hinweise:
- Inline-Assembler ist architekturspezifisch und sollte mit Bedacht verwendet werden.
- Beachten Sie die korrekte Verwendung und Zuordnung der Register.
- __asm__ in GNU C ist flexibil und mächtig, aber prüfen Sie, ob optimierende Compiler Ihren Code nicht verändern.
Reiner Assembler
Um den Teil für __asm__ als reinen ARM64-Assemblercode zu übersetzen, sehen Sie sich folgendes Beispiel an:
ASM Copy code .global add_numbers // Globale Funktion
add_numbers:
add w0, w0, w1 // Addiere die Werte in w0 und w1, das Ergebnis wird in w0 gespeichert ret // Rückkehr zur aufrufenden Funktion, wobei das Ergebnis in x0 verbleibt
In diesem reinen Assemblercode läuft die Funktion add_numbers:
Die Funktion verwendet die Register w0 und w1 als Eingaben, gemäß dem ARM64-Aufrufkonventionen, wo das erste Argument in w0 und das zweite Argument in w1 übergeben wird. Der Befehl add w0, w0, w1 führt die Addition durch und speichert das Ergebnis in w0. ret kehrt zur aufrufenden Funktion zurück, wobei w0 (d.h. x0 für 64-Bit) das Ergebnis enthält.
Bibliotheken verwenden
Um C-Bibliotheken in ARM64-Assembler zu verwenden, müssen Sie sicherstellen, dass die Bibliotheken korrekt eingebunden und die entsprechenden Funktionen extern deklariert werden.
Beispiel: Verwendung der math.h-Bibliothek:
Beispiel in C:
// mymath.c
#include <math.h>
double sqrt_wrapper(double x) {
return sqrt(x);
}
Beispiel in Assembler:
.extern sqrt_wrapper
.global _start
_start:
ldr x0, =9.0 // Lade 9.0 in x0
bl sqrt_wrapper // Rufe sqrt_wrapper auf
// Exit system call
mov x8, #93 // syscall number for exit
mov x0, #0 // exit code
svc 0
Kompilierung und Linken:
gcc -c mymath.c
as -o myprogram.o myprogram.s
gcc -o myprogram myprogram.o mymath.o -lm
Wichtige Hinweise:
Deklarieren Sie alle externen Funktionen, die Sie verwenden möchten. Stellen Sie sicher, dass die Bibliotheken korrekt beim Linken einbezogen werden (-lm beispielsweise für die Math-Bibliothek).