externen asm code im VC++
-
Keine Ahnung ob ich in diesem Teil des Forums richtig mit meiner Frage bin. Fall nicht bitte verschieben ...
Ich würde gerne eine Assemblerfunktion im VC++ einbinden. Diese soll aber kein Inline-ASM sein, sondern extern über eine OBJ-Datei eingebunden werden. Ich erhalte folgenden Fehler:main.obj : error LNK2001: unresolved external symbol _EncodeBase64@12
Mein Sourcecode sieht folgendermaßen aus:
#include <stdlib.h> #include <time.h> extern "C" int __stdcall EncodeBase64(void *DataToEncode, int DataSize, void *OutputBuffer); int main() { char rawdata[256]; char buffer[256*4/3+3]; srand((unsigned)time(NULL)); for(int i=0;i<256;i++) rawdata[i]=(char)rand(); int nBytesEncoded=EncodeBase64((void *)&rawdata,sizeof(rawdata),(void *)&buffer); return 0; }
Mein Assemblercode sieht folgendermaßen aus:
.586p .model flat .data counter1 DD ? .code public EncodeBase64 EncodeBase64 proc push ebp mov ebp,esp mov esi,[ebp+08h] mov ecx,[ebp+0Ch] mov edi,[ebp+10h] mov counter1,0 cmp edi,esi jnz do_encode xor eax,eax pop ebp ret 0ch do_encode: call above_charset charset DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DB "abcdefghijklmnopqrstuvwxyz" DB "0123456789+/" above_charset: pop ebx push edi main_loop: dec ecx js end_base64 jnz not_one movzx edx,byte ptr [esi] bswap edx inc ecx inc ecx call encode_bytes mov ax,'==' stosw jmp end_base64 not_one: dec ecx jnz not_two movzx edx,word ptr [esi] bswap edx xor ecx,ecx mov cl,3 call encode_bytes mov al,'=' stosb jmp end_base64 not_two: dec ecx push ecx movzx edx,word ptr [esi] bswap edx mov dh,byte ptr [esi+2] xor ecx,ecx mov cl,4 call encode_bytes pop ecx add esi,3 jmp main_loop end_base64: pop ecx sub ecx,edi neg ecx mov eax,ecx pop ebp ret 0Ch encode_bytes: cmp counter1,4Ch jnz no_crlf mov ax,0A0Dh stosw mov counter1,0 no_crlf: xor eax,eax rol edx,6 mov al,dl and al,00111111b xlatb stosb inc counter1 loop encode_bytes ret EncodeBase64 endp
Der ASM code wurde mit MASM folgendermaßen assembliert:
ml.exe /c /coff /Cx base64.asm
Die base64.obj Datei wurde auch ins Projekt aufgenommen. Hat jemand eine Ahnung was da falsch sein könnte?
-
Unter Windows scheint es Konvention zu sein, dass C-Funktionen in Assembelr immer ein _ davor haben. uVersuch mal deine ASM-Funktion als _EncodeBase64 zu exportieren. Wahrscheinlich musst du soagr die MSVC-Konvention benutzen, die es verlangt, dass noch ein @ kommt und dann die Größe aller Parameter zusammen. Wenn ersteres also nicht klappt, versuch den Export als _EncodeBase64@12
-
Das mit dem Unterstrich habe ich auch irgendwo gelesen und auch ausprobiert, aber es klappte nicht. Es kam die gleiche Fehlermeldung, nur dass es diesmal nicht "_EncodeBase64@12" hieß, sondern "__EncodeBase64@12". Das mit dem @ kann ich allerdings mal testen.
-
also bei __stdcall wird immer der name verhunzt *g*
-
Hmm ... Ok, daran könnte es natürlich auch liegen. Logisch wäre das nicht, aber ich werde ich dann mal mit einer anderen Aufrufkonvention versuchen. Dankeschön.
-
Hallo malfunction,
wie hat es denn bei Dir noch funktioniert?
Hänge im Moment an dem selben Punkt (wenn auch mit einer wesentlich einfacheren Prozedur ohne Parameter) fest und wollte nicht noch einen Thread zu diesem Thema starten.Pablo
-
Das Problem ist, dass Dein Funktionsprototyp im Assemblermodule stimmen muss. Definiere die Funktion mit
EncodeBase64 proc indata:DWORD, datalength:DWORD, outdata:DWORD
In der .model-Direktive muss nun auch stdcall stehen.
Statt mov register, [ebp + Offset] kannst Du nun die Variablennamen verwenden(indata, datalength, outdata), also mov esi, indata, etc. Den Stackframe NICHT mehr selber setzen, das macht der Assembler nun automatisch. Auch wird der ret-Befehl automatisch angepasst, um die Parameter beim Rücksprung vom Stack zu holen(hier also ret 12).
Wenn Du in der PROC-Anweisung die Variablen und deren Typen nicht angibst, so steht in der OBJ-Datei _EncodeBase@0 als Funktionsname. Der C-Linker erwartet laut Deklaration aber _EncodeBase64@12!
Ciao...
[ Dieser Beitrag wurde am 01.07.2003 um 18:56 Uhr von Kal El editiert. ]
-
Wo ist der Haken?
mode.asm:
MODEL COMPACT
.Code
public mode13 ;auch versucht: _mode13@0 oder _mode13
mode13 proc ; ""
mov ax,0013h
int 10h
ENDP
ENDmain.cpp:
extern "C" void mode13(); //oder mit _stdcall
//oder auch _mode13(void)mit/ohne _stdcall
void main(){
mode13();
}Nur leider wollen sich sämtliche Kombinationen nicht linken lassen.
So, und jetzt ein Bierchen trinken gehen und sämtlichen Gram vergessen.
Pablo
-
Also ich habe Dein kleines Beispiel mit Turbo C++ 1.01(kann man bei Borland kostenlos herunterladen) und Tasm erstellt. Dabei gab es keine Probleme.
.model COMPACT, C .code public mode13 mode13 proc mov ax, 0013h int 10h mode13 endp end
Übersetzt mit:
TASM /ml /m3 /zn mode.asmDas C-Programm:
extern "C" void mode13(void); int main() { mode13(); return 0; }
Das C-Programm habe ich aus der IDE heraus compiliert. Dabei ist zu beachten, dass das Speichermodell des C-Programms mit dem Assemblermodul übereinstimmt.
Ciao...
-
@Kal El,
Dankeschön für den Tipp, es lässt sich linken(wenngleich auch die .exe eine Speicherverletzung oder Ähnliches erzeugt).
Obwohl ich was für den Retro-Style übrig hab ist Turbo c++ 1.01 kein wirklich komfortables Werkzeug(der Download war nicht umsonst im Borland-Museum), v.a. auch in Anbetracht dessen, dass man beim Herumprobieren nach jeder kleinen Codeschnipseländerung wieder die Kommandozeile zum Linken bemühen muss. Da ist es schon bequemer, beim MSVS STRG+F5 zu drücken. Aber der Inline Assembler ist wirklich klasse, da kann man sich das Linken mit den Assembler-generierten .obj
eigentlich sparen und die movs und ints und pops gleich direkt in C++ einbinden.
schönen Tag wünscht
Pablo
-
Nun ja, Dein Beispielcode verwendet einen BIOS-Aufruf, um den VGA-Modus 013h zu setzen. Das geht nur unter DOS. Daher hatte ich einen alten DOS-C-Compiler verwendet. Mit Visual C++ bekommst Du Probleme, weil hiermit nur Win32-Exes erzeugt werden können. Und unter Win32 sind diese Realmode-Interrupts ja bekannterweise nicht gestattet.
Dir auch einen schönen Tag
Ciao...