externe Funktionsdeklaration in MASM
-
Einen wunderschönen guten Tag,
ich habe ein Problem^^. Und zwar muss ich mich - notgedrungen - in MASM einarbeiten. Ich habe dort (beispielhaft) folgende Art der Deklaration von externen Funktionen gefunden
EXTERN _Proc1:PROC EXTERN _Proc2:BYTE EXTERN _Proc3:DWORD
So, jetzt die große Frage:
Was soll denn bitte schön ":BYTE" oder ":DWORD" heißen? Sind das die Rückgabetypen oder was?Es wäre wirklich super, wenn mir jemand diese Frage beantworten könnte. Da ich allerdings noch mehr Dinge hätte, wäre ein Buchtipp (explizit für MASM) nicht schlecht. Ich habe von "Power Programming with Microsoft Macro Assembler" von Microsoft Press gehört, kann das Buch aber weder bei Amazon, Libri, noch bei Microsoft Press selbst finden ...
Hat da vielleicht jemand Rat für mich?
-
_Proc1 ist als externe Prozedur deklariert, _Proc2 und _Proc3 als Variablen vom type BYTE und DWORD (widersinnig zum Namen). An sich sollte man die flexiblere Anweisung EXTERNDEF für Variablen und PROTO für Funktionen benutzen. (EXTERN setz voraus, das die Variable/Prozedur auf jeden Fall in irgendeinem der verlinkten Module mit PUBLIC deklariert werden).
Zum Thema Bücher: google mal nach “ MASM Programmer's Guid“.
Wenn du weitere Fragen hast, kann ich dir nur das masmforum empfehlen (hat auch eine Suchfunktion ;)): http://www.masm32.com/board/index.php
-
x86-64 schrieb:
_Proc1 ist als externe Prozedur deklariert, _Proc2 und _Proc3 als Variablen vom type BYTE und DWORD (widersinnig zum Namen).
_Proc1 war mir auch klar. Aber die Sachen die in meinem Code zum Teil als "BYTE" oder "DWORD" markiert waren, sind definitiv auch Funktionen.
Inwiefern ist denn "PROTO" flexibler als "EXT(E)RN"? Ich will ihm doch nur mitteilen, dass ich da eine Funktion habe, die in einem anderen Modul definiert worden ist und die ich per Linker später einbinden werde.
EDIT:
Ich hab jetzt mal was versucht und bekomm allerdings den Compiler-Error "syntax error : PROTO" ....686P .XMM .model FLAT, PASCAL .code PROTO @@DynArraySetLength:NEAR [...]
-
FrEEzE2046 schrieb:
Inwiefern ist denn "PROTO" flexibler als "EXT(E)RN"? Ich will ihm doch nur mitteilen, dass ich da eine Funktion habe, die in einem anderen Modul definiert worden ist und die ich per Linker später einbinden werde.
Mit PROTO/EXTERNDEF kann man in allen Modulen benutzen, egal ob die Funktion im gleichen Modul deklariert wird oder nicht -> man kann z.B. die gleiche include-file für alle Module verwenden - das würde mit EXTERN/PUBLIC nicht gehen. In deinem fall macht das aber kein unterschied.
FrEEzE2046 schrieb:
Aber die Sachen die in meinem Code zum Teil als "BYTE" oder "DWORD" markiert waren, sind definitiv auch Funktionen.
Wie werden die aufgerufen? : call PROC ptr _Proc2
PROTO benutzt man so:
FunktionsName PROTO [C,STDCALL,..] [VarName]:TYPE,...
(Eckige Klammern = optional)
PS: near brauchst du nicht - wir bewegen uns immer im gleichen Segment
-
Sry, aber was meinst du mit "Funktionsname" und dann nochmal "Var Name"?
Hab's jetzt so gemacht:@@DynArraySetLength PROTO [PASCAL]
Jetzt kommt: "Syntax error : [". Ohne die geht's aber.
-
sry, war etwas verwirrend:
Am Beispiel MessageBox (WINAPI)MessageBoxA PROTO STDCALL hWnd:DWORD,lpText:DWORD,lpTitel:DWORD,dwFlag:DWORD
mit proto kann man also auch die Funktionsparameter angeben -> dies braucht man wenn man den Äußerst konvertiblen invoke-Macro benutzen will. Hier mal ein Beispiel mit mehreren Möglichkeiten einen Funktionsprototypen an zu geben:
.686p .model flat, stdcall ; stdcall als default-calling convention vereinbart option casemap :none ; groß/klein Schreibung unterscheiden .mmx .xmm includelib User32.Lib ; findet man z.B. im Windows SDK oder, im MASM32-package (absolutes Muss für masm :)) ; 1 MessageBoxA PROTO STDCALL hWnd:DWORD,lpText:DWORd,lpTitel:DWORD,dwFlags:DWORD ; 2 ;MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD ; 3 ;EXTERNDEF MessageBoxA@16:PROC ; 4 ;PR4 typedef PROTO :DWORD,:DWORD,:DWORD,:DWORD ;EXTERNDEF MessageBoxA:PR4 .data t1 db "hallo Welt",0 t2 db "bla",0 .code start: ; für 1,2 und 4 invoke MessageBoxA,0,OFFSET t1,OFFSET t2,0 ; nur für 3 ;push 0 ;push OFFSET t2 ;push OFFSET t1 ;push 0 ;call MessageBoxA@16 ret end start
Zeig doch mal deinen Programmcode - das macht es vieleicht einfacher dir zu helfen ...
-
x86-64 schrieb:
Zeig doch mal deinen Programmcode - das macht es vieleicht einfacher dir zu helfen ...
Das würde ich ja machen; bringt aber nur dann was, wenn du Delphi kannst.
Ich habe noch gar keinen richtigen Programmcode (wie gesagt, ich will dass ja alles erst noch umstellen).Ich habe aber zum Test versucht die Procedure SetLength aufzurufen (dass ist eine Delphi (Pascal) Routine).
Warum brauche ich dass? Weil ich zum Teil arrays zurückliefern muss und dass ist in Delphi immer ein ziemliches gemurkse.Hier mal mein (extra ganz stupider) Code:
TITLE DelphiTest.asm .686P .XMM .model FLAT, PASCAL .code _DynArraySetLength PROTO PASCAL public SetLen ; procedure SetLen(var B : array of Byte; val : Byte); SetLen PROC call _DynArraySetLength ; parameter liegen korrekt SetLen ENDP END
unit TestUnit; interface uses SysUtils; procedure SetLen(var B : array of Byte; val : Byte); implementation procedure SetLen(var B : array of Byte; val : Byte); external; {$L DelphiTest.obj} end.
Das führt zu der Meldung:
Ungenügende Forward- oder External-Deklaration von _DynArraySetLengthDas Problem ist mir natürlich klar, fragt sich nur warum der Linker es nicht finden kann. SetLength(ruft im Endeffekt DynArraySetLength auf) ist deklariert in der System.pas, die man aber gar nicht direkt einbinden kann ... ziemlich nervig.
-
mhh.. (Borland) Delphi arbeitet doch mit fastcall !?.
-
masm unterstützt fastcall nicht, jwasm(masm clon) aber schon.
Die Deklaration müsste dann so aussehen:
SetLen PROTO FASTCALL bla:ptr BYTE,val:BYTEEin andere Möglichkeit wäre es, wenn du die calling convention in delphie zu z.B. sdtcall änderst.
-
x86-64 schrieb:
masm unterstützt fastcall nicht, jwasm(masm clon) aber schon.
Die Deklaration müsste dann so aussehen:
SetLen PROTO FASTCALL bla:ptr BYTE,val:BYTEEin andere Möglichkeit wäre es, wenn du die calling convention in delphie zu z.B. sdtcall änderst.
Hallo und Danke für eure Antworten.
1. Delphi hat nicht mehr Pascal als Standardaufrufkonvention, sondern Register. Dies entspricht am ehesten dem, was in C/C++ der fastcall ist. Die Parameter werden in Reihenfolge ins EAX, EDX und ECX Register gelegt.
2. Ich kann die Aufrufkonvention in Delphi sogar in CDECL ändern; aber natürlich nicht von den bereits bestehenden Funktionen (wie eben SetLength). Die ist weiterhin Register.
Ich vermute jetzt mal, dass die Meldung nicht wirklich daraufhin deutet, dass er nur differierende Calling Conventions gefunden hat, aber in Delphi weiß man ja nie
Ich schaue es mir mal an.
-
So,
ich hab jetzt einfach mal versucht mit meinem eigenen (zunächst simplen) Code-Implementationen weiter zu machen.
Ich glaube mir reicht das Hilfe-Dokument allein nicht aus; dass hat ja stellenweiße nicht mehr viel mit Assembler zu tun, was da gemacht wird.
Wie genau implementiere ich jezt eine Funktion? Angenommen ich habe eine Funktion IntCmp, die aus einem C-Programm aufgerufen werden soll. Ich bin da jetzt folgendermaßen drangegangen:
TITLE Compare.asm .686P .XMM .MODEL FLAT, C PUBLIC IntCmp:PROC .CODE _TEXT SEGMENT _lho$ = 8 _rho$ = 12 _dwSize$ = 16 IntCmpEx PROC push edi push esi push ebx xor ebx, ebx test ecx, 0xFFFFFFFC lea esi, DWORD PTR [eax+ecx-1] lea edi, DWORD PTR [edx+ecx-1] jz Bytes sub esi, 3 sub edi, 3 DWORDs: mov eax, DWORD PTR [esi] mov edx, DWORD PTR [edi] cmp eax, edx jnz Exit sub esi, 4 sub edi, 4 sub ecx, 4 test ecx, 0xFFFFFFFC jnz DWORDs test ecx, ecx jz Exit add esi, 3 add edi, 3 Bytes: movzx eax, BYTE PTR [esi] movzx edx, BYTE PTR [edi] dec edi dec esi cmp eax, edx jnz Exit dec ecx jnz Bytes Exit: seta bl sbb ebx, 0 mov eax, ebx pop ebx pop esi pop edi IntCmpEx ENDP _TEXT ENDS END
Bekomme aber folgende Fehler:
(8): Error A2008 : Syntax Error : : (23): Error A2206 : Missing operator in expression (42): Error A2206 : Missing operator in expression
23 und 42 sind die Zeilen mit der Hexzahl. Wie muss ich denn Hexzahlen in MASM angeben? Mit einem nachgestellten 'h' mag er es auch nicht (unbekannter Bezeichner).
Was ich auch noch gesehen habe ist folgende Art der Funktionsdeklaration:
myproc PROC FAR C PUBLIC <callcount> USES di si, argcount:WORD, arg2:VARARG
was muss ich da denn alles angeben und vorallem: Was gibt denn USES an? Alle verwendeten Register?
-
Zu USES: Nach uses folgen Register, die automatisch beim Eintritt in die Prozedur auf den stack gepusht werden sollen. Überall wo RET oder LEAVE steht, werden dann die Argument wieder vom stack geholt (pop). Für eine normale Funktionsdefinition reicht:
Name proc Parameter1:DWORD,Parameter2:DWORD ...
DWORD = nur Beispielhaft, man könnte auch byte,word,real4,real8... verwenden.
Der Prototyp dazu sähe gleich aus, bis auf das man 'proc' durch 'proto' ersetzt.; die von masm erstellte object file (*.obj) muüsste man dem c-linker mitteilen .686P .XMM .MODEL FLAT, C IntCmpEx PROTO ;hat keine Parameter? .CODE ; zwischen den Segment wechselt man mit .code .data .data? .const start: ; _lho$ = 8 _rho$ = 12 _dwSize$ = 16 ; Allgemeiner Hinweis: ; Masm verfügt über hochsprachen Konstrukte wie .if/.eleif/.ele/.endif .while/.endw .repeat/.until .break .continue ; Bsp: .if eax >= edx && ecx != MyDwordVar ; ... ; .endif ; Vergleichsoperatorn: '!=', '<=' , '>=' , '==' ,'<' , '>' ; Sonstige: Negieren : '!' ; Bitverknüpfung : 'a&b' = A AND B ; : 'a|b' ? A OR B ; - Mehrere Bedingungen verknüpft man mit '&&' und '||' ; - Klammern kann man auch: ((a!=b) || !(b>=c)) ... ; - Flags können getestet werden: ZERO?,CARRY?,OVERFLOW? ... ; Bsp: .if !ZERO? ; wenn zero-flag nicht gesetzt ; - Es können Register, Speicheroperanten und Konstanten verglichen werden, aber nie 2 Speicheroperanten miteinaner ; - die Operanten die verglichen werden, müssen natürlich gleich groß sein ; - u.v.m. ;) IntCmpEx PROC ; hat keine Parameter ? push edi push esi push ebx xor ebx, ebx test ecx, 0FFFFFFFCh ; Zahlen dürfen nie mit Buchstaben anfangen -> 0 davor falls nötig ; hex-dezimale Zahlen haben das "h" Suffix, (binär: 'y' oder 'b') lea esi, DWORD PTR [eax+ecx-1] lea edi, DWORD PTR [edx+ecx-1] jz Bytes sub esi, 3 sub edi, 3 DWORDs: mov eax, DWORD PTR [esi] mov edx, DWORD PTR [edi] cmp eax, edx jnz Exit sub esi, 4 sub edi, 4 sub ecx, 4 test ecx, 0FFFFFFFCh jnz DWORDs test ecx, ecx jz Exit add esi, 3 add edi, 3 Bytes: movzx eax, BYTE PTR [esi] movzx edx, BYTE PTR [edi] dec edi dec esi cmp eax, edx jnz Exit dec ecx jnz Bytes Exit: seta bl sbb ebx, 0 mov eax, ebx pop ebx pop esi pop edi ret ; ohne RET wird das nichts ;) IntCmpEx ENDP END start
-
Problematisch ist bei mir gerade eher, dass ich nicht in der Lage bin die entstandene Objektdatei im Visual Studio zu linken; mit GCC geht's.
Hab das Ganze so gemacht:
#pragma comment(linker, "/include:Compare.obj") #define LPCVOID const void* extern int IntCmp(LPCVOID lho, LPCVOID rho, LPCVOID lpSize); int main(int argc, char** argv, char** env) { int i1 = 7, i2 = 9, res = 0; res = IntCmp(&i1, &i2, sizeof(i1)); return 0; }
Er sagt immer
unresolved external symbol '_IntCmp'
Zudem bekomme ich es nicht hin zwei Funktionen zu deklarieren.
TITLE SimpleTest.asm .686P .XMM .MODEL FLAT Proc1 PROTO Proc2 PROTO .CODE Proc1 PROC ; ; Proc1 ENDP Proc2 PROC ; ; Proc2 ENDP END
MASM hat damit kein Problem. So lange ich nur Proc1 hier deklariere hat Delphi nichts dagegen (in Delphi sind beide Funktionen als extern deklariert und das Obj-File gelinkt). Wenn beide drin sind, dann sagt er bei beiden ungenügende external Deklaration bzw. "Falsche globale Symboldefinition".
Was mache ich falsch?
-
Vielleicht fehlt da einfach ein extern "C":
... extern "C" { extern int IntCmp(LPCVOID lho, LPCVOID rho, LPCVOID lpSize); } ...
-
Kennst du dich vielleicht auch mit Delphi aus?
Habe ein Objekt-File, dass ich mit MASM folgendermaßen kompiliert habe:ml /c /omf "source.asm"
Das ganze habe ich dann per "{$LINK "Pfad\source.obj"} in Delphi gelinkt und bekomme aber immer die Meldung: "Falsches Dateiformat".
-
das omf-Format von Delphi weicht etwas vom Standard ab ... Im Manual zu Agner Fog's ObjConverter steht etwas dazu.
Aber warum schreibst du nicht einfach eine Dll in masm? - ist doch am einfachsten.
-
dll schrieb:
das omf-Format von Delphi weicht etwas vom Standard ab ... Im Manual zu Agner Fog's ObjConverter steht etwas dazu.
Aber warum schreibst du nicht einfach eine Dll in masm? - ist doch am einfachsten.Jo, hab ich jetzt auch gemacht. Es ist wirklich nur rum gemurkse, wenn man mit Delphi OMF-Dateien arbeiten muss. Entweder bin ich zu blöd, ... auf jeden Fall geht es mal und mal geht es nicht ... Irgendwie scheint es, wie du schon sagtes, sich nicht wirklich an den Standard zu halten.