Variable wird bei Aufruf verändert
-
Hallo und Guten Abend,
ich habe ein Problem beim Aufruf der Folgenden Funktion:
PROCEDURE SeekEx(VAR afile; aPos: INT64); [asm]ASM push ebp mov ebp, esp push ebx mov ebx, DWORD PTR [afile] mov eax, DWORD PTR [aPos+4] // low 4 Byte of aPos mov edx, DWORD PTR [aPos+8] // high 4 Byte of aPos mov ecx, TTextRec([ecx]).Mode sub ecx, fmInput cmp ecx, fmInOut-fmInput ja @@FileNotOpen mov ecx, TFileRec([ebx]).RecSize mul eax push FILE_BEGIN // set dwMoveMethod to file begin push 0 // no need for new file pos information push edx // high push eax // and low 4 Byte of lDistanceToMove push ebx // push aFile to stack call SetFilePointerEx add esp, 20 // clean stack cmp eax, 0 // function returns false jz XInOutError jmp @@Exit @@FileNotOpen: mov eax, 103 jmp SetInOutRes @@Exit: pop ebx mov esp, ebp pop ebp[/asm] END;
Zur Erläuterung: In Delphi bedeutet der Parameter "VAR afile" so viel wie ein void* der dereferenzierungslos verwendet werden kann ...
Die Funktion funktioniert sonst einwandfrei. Aber nach dem Funktionsaufruf ist afile verändert worden. Da es sich hierbei um ein File-Handle handelt, ist dass äußerst problematisch ...
-
Ich verstehe deine Funktion zwar nicht wirklich, aber wenn ich sie mir so anschaue, dann fällt mir folgendes auf:
mov eax, DWORD PTR [aPos+4] // low 4 Byte of aPos mov edx, DWORD PTR [aPos+8] // high 4 Byte of aPos
Wenn die ersten 4 Bytes von aPos an der Adresse von aPos + 4 liegen, was liegt dann an der Adresse von aPos selbst?
-
Falls "SetFilePointerEx" aus der WinAPI stammt, dann räumt "SetFilePointerEx" den Stack selbst auf (stdcall) :
(...) push FILE_BEGIN push 0 push edx push eax push ebx call SetFilePointerEx ; add esp, 20 // <- weg damit :) (...)
-
/rant/ schrieb:
Wenn die ersten 4 Bytes von aPos an der Adresse von aPos + 4 liegen, was liegt dann an der Adresse von aPos selbst?
Ich verstehe deine Frage. Aber der Code kommt aus dem Inline Assembler von Delphi. Dabei habe ich festgestellt (und die korrekten Werte geben mir recht), dass aus irgendeinem - mir unerklärlichen Grund - ein 8 Byte Datentyp immer als 12 Byte Typ auf den Stack geschrieben wird.
Bitte frag mich nicht warum. Die Werte von den ersten 4 Byte an der Adresse von aPos ergeben auch leider keinen Aufschluss darauf. Es sieht einfach nach "Schrott"-Werten aus.Aber mein Kollege und ich, haben auch nicht schlecht gestaunt. Wir hatten einige Zeit gerätselt, warum wir nur Schrott-Werte bekommen, bis uns dass aufgefallen war ... irgendeine Delphi Macke.
Angenommen Int64 mit Wert 88 auf dem Stack:
Byte 1: Schrott
Byte 2: Schrott
Byte 3: Schrott
Byte 4: Schrott
Byte 5: 88
Byte 6: 0
Byte 7: 0
Byte 8: 0
Byte 9: 0
Byte 10: 0
Byte 11: 0
Byte 12: 0Es kam der Delphi 5 Compiler zum Einsatz. Vielleicht ist das "Problem" mittlerweile behoben. Wenn du Delphi 5 zur Hand hast, kannst du dich gerne selbst davon überzeugen.
Ich bin darauf gekommen, weil mir aufgefallen war, dass er mir die High Bytes "halbwegs korrekt" anzeigt, auch wenn es eigentlich die Low Bytes sein müssten. Ich dachte erst, dass die beiden DWORD-Paare einfach vertauscht wären. Aber wie gesagt, in den ersten 4 Byte steht nur Mist.
Dann hab ich einfach mal so aus Verzweiflung geschaut, was bei aPos + 8 liegt ... und siehe da: Unsere restlichen 4 Byte ... schon sehr interessant+gjm+ schrieb:
Falls "SetFilePointerEx" aus der WinAPI stammt, dann räumt "SetFilePointerEx" den Stack selbst auf (stdcall) :
Ja, du hast recht. Daran hatte ich selbst nicht gedacht. Dass sollte das Problem lösen. Vielen Dank soweit
-
FrEEzE2046 schrieb:
...dass aus irgendeinem - mir unerklärlichen Grund ...
der Grund heißt (vermutlich) "Borland fastcall / Delphi register call": die ersten drei Parameter werden durch die Register eax,edx,ecx übergeben, weiter werden auf dem Stack abgelegt (alle von links nach rechts). IIRC werden alle größeren Datentypen als pointer übergeben (außgenommen real4/8/10 - die werden direkt auf den Stack abgelegt). In deinem Fall hieße das dann, dass eax=aFile und edx=Zeiger auf INT64 . Aber vielleicht irre ich mich auch – kann meiner Aussagen leider auch nicht Überprüfen, da ich keine Delphi habe…
-
__asm{} schrieb:
der Grund heißt (vermutlich) "Borland fastcall / Delphi register call": die ersten drei Parameter werden durch die Register eax,edx,ecx übergeben, weiter werden auf dem Stack abgelegt (alle von links nach rechts). IIRC werden alle größeren Datentypen als pointer übergeben (außgenommen real4/8/10 - die werden direkt auf den Stack abgelegt). In deinem Fall hieße das dann, dass eax=aFile und edx=Zeiger auf INT64 . Aber vielleicht irre ich mich auch – kann meiner Aussagen leider auch nicht Überprüfen, da ich keine Delphi habe…
Nicht ohne Grund kommt es ja von "Turbo Pascall". Die Standard Aufrufkonvention ist mir schon bekannt.
Es werden aber nur die ersten Parameter in die genannten Register geschrieben, die der Wortbreite des Prozessors entsprechen. Daher sollte hier kein 8 Byte großer Typ in die Register geschrieben werden.Dass von allen größeren Typen die Adresse übergeben wird, wäre eine Vermutung. Es scheint jedoch nicht so zu sein, sonst würde meine Erläuterung oben nicht zustande kommen können.
Generell ist es sowie so wichtig (für verschiedene Befehle), dass ein 8 Byte großer Typ im EAX und EDX Register gehalten wird. Somit würde es keinen Sinn machen im EDX-Register einen Pointer auf Int64 zu haben und es ist defacto auch nicht der Fall gewesen. Eine Erklärung wäre fein, aber da die Funktionalität vorhanden ist, muss ich es nicht unbedingt wissen.
Ich kann leider erst am Montag prüfen ob der stdcall des Problems Ursprung war, da ich privat keinen Delphi-Compiler zur Verfügung habe.