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



  • @malfunction:

    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
    END

    main.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



  • @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.asm

    Das 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



  • @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...


Anmelden zum Antworten