Assembler Funktion in C Code verwenden
-
Ich möchte eine .asm Datei mit einer Funktion, die ich in assembler geschrieben habe in meinem Code aufrufen.
-
compiler, prozessor, OS?
-
kann dein c inline asm?
wenn ja, dann kannste's so machen:int funktion (int arg) { asm // oder _asm { // hier hinein der asm code mov r0,arg // beispiel // zugriff auf argument und rückgabewert // musste im manual nachschlagen } }
-
Ich möchte Assembler Code in meinem C Code als Funktion verwenden.
mit dem Befehl _asm funktioniert es nur auf diese Art:
__asm( "add %0, %1, %2\n" "\tmul %0, %0, %3": "=d"(*presult): "d"(a), "d"(b), "d"(c) );
dabei müssen die Variablen am Ende des gesamten eingefügten Codes zugewiesen werden, und das macht das ganze sehr unübersichtlich und aufwendig zu programmieren.
Ich versuchte den Assemblercode in eine .asm Datei auszulagern und diese im C Programm aufzurufen.
Wie kann ich dabei die Parameter der Funktion übergeben bzw. wie werden im Asm Code die Variablen richtig zugewiesen?
-
guck doch einfach, wo der assembler-code die parameter erwartet: entweder auf dem stack oder in registern.
je nach moeglichen calling-convention deines c-compilers wirst du aber vermutlich sowieso nicht um stack-uebergabe herum kommen.
-
Nun, wenn du den Code in eine separate asm Datei auslagern willst, brauchst du auf jeden Fall einen Assembler, wie nasm, yasm, fasm oder was auch immer. Dieser erstellt dann die Objektdatei, welche du im richtigen Format deinem Linker mitgeben musst. Für exportierte Symbole eignet sich logischerweise C Name-Mangling. Dazu solltest du die entsprechenden Symbole, wie zB den Namen deiner Funktion, in der Assemblerdatei entsprechend handeln. Bei nasm verwendet man zB das Schlüsselwort global, wie es bei deinem Assembler aussieht, entnimmst du am besten der Dokumentation. Beim C Name-Mangling ist es wichtig, dass du die Symbole mit einem führenden Unterstrich exportierst.
Machen wir das ganze mal an einem simplen Beispiel. Für Assembler musst du dir natürlich immer darüber im Klaren sein, auf welchem System du arbeitest. Wir nehmen hier mal 32 Bit Windows. In deinem C Modul brauchst du natürlich die Deklaration der Funktion.
#ifdef __cplusplus // falls die Funktion auch unter C++ genutzt werden soll extern "C" { #endif void __cdecl foo(int x, int y); // unser Funktionsprototyp #ifdef __cplusplus } #endif
Man beachte hier das Schlüsselwort __cdecl. Dieses spezifiziert die Aufrufkonvention und bestimmt, wie Parameter übergeben werden. __cdecl ist idR default, du kannst aber auch eine andere Aufrufkonvention nehmen. Du musst dann aber auch den asm Code entsprechend anpassen. __cdecl gehört übrigens nicht zum Standard, aber jeder Compiler sollte damit etwas anfangen können. Du kannst __cdecl auch weglassen, solltest dann aber sicherstellen, dass __cdecl immer die default Aufrufkonvention ist.
Als nächstes kommt dein Assembler Modul. Ich nehme für das Beispiel nasm.use32 global _foo ; foo exportieren
Kommen wir zur eigentlichen Funktion. Parameter landen auf dem Stack, also müssen wir sie von dort lesen. Weil sich das Stackregister (esp) jederzeit ändern kann, zB durch push und pop, nimmt man normalerweise das Register ebp, um die Daten anzusprechen. Demzufolge sollte man darauf, ebp niemals zu verändern, damit die Offsets immer passen. Ein typischer Funktionsrahmen sieht wie folgt aus:
; unsere Funktion _foo: push ebp mov ebp, esp ; hier der eigentliche Code pop ebp ret
Mit [ebp+...] können wir nun im Funktionsrumpf auf den Stack zugreifen. [ebp+0] bis [ebp+3] ist der gespeicherte Wert von ebp selbst (push). [ebp+4] bis [ebp+7] ist die Rücksprungadresse der Funktion. Ab [ebp+8] beginnen unsere Parameter. [ebp+8] bis [ebp+11] wäre dann x, [ebp+12] bis [ebp+15] y.
Willst du zB den Wert von y in eax kopieren, schreibst du folgendes:mov eax, [ebp+12]
Zu beachten ist noch, dass ich für das Beispiel hier Intel Syntax verwendet habe. Du kannst natürlich auch einen Assembler mit AT&T Syntax verwenden.
-
Wie wird der Funktionsprototyp auf den Körper im asm File zugewiesen?
Verwende bei mir speziell ein makefile um den Maschinencode für den Controller zu generieren. Habe mehrere C-Funktionen über die Header-Datei eingebunden. Damit sind sie auch im Projekt vorhanden. Aber die .asm Datei wird mit dem Prototypen in der Header nicht verwendet. Gibt es da einen speziellen Befehl, oder muss ich das im Makefile einstellen.
Oder muss ich vorher selber ein Objectfile daraus machen und es selber mit dem Makefile einbinden. Es muss halt ein Interface zwischen dem .asm bzw. .o File und dem Projekt bzw. dem Funktionsprototypen erstellt werden.
-
Comet7 schrieb:
Wie wird der Funktionsprototyp auf den Körper im asm File zugewiesen?
das macht der linker.
du hast ja einen prototypen gemacht:
void __cdecl foo();
und im ASM code die funktion exportiert:
global foo;
__cdecl ist die calling convention - die musst du halt deinem compiler entsprechend anpassen. ebenso den asm code zum exportieren.
der linker checkt es sich dann selbst dass du im C code die funktion foo referenzierst und dein ASM code die funktion foo exportiert.