Windows - ASM inline de Delphi
Bonjour,
Je suis en train de développer des thunks en assembleur qui doivent ajouter un paramètre à un appel de méthode, et ce dans les quatre principales conventions d'appel de Delphi : register, stdcall, pascal et cdecl. Pour les trois premières, aucun problème. Pour la cdecl, c'est plus délicat
Je dois en effet gérer les exceptions qui pourraient être déclenchées dans la méthode appelée, afin de libérer correctement mes infos. Cependant, je ne peux pas me permettre de stocker la EXCEPTION_REGISTRATION dans la pile, tout simplement car je ne peux pas modifier la pile (en dehors du paramètre que j'ajoute) !
En fouillant pas mal sur le net, j'ai appris beaucoup sur la gestion des exceptions en assembleur, et je suis arrivé à ce code-ci (assembleur inline du Delphi) :
Ca fonctionne très bien... Tant qu'aucune exception n'est lancée
Code Delphi : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 type TCDeclReturnInfo = packed record PreviousExceptReg : Pointer; Handler : Pointer; EBPContents : LongWord; ReturnAddress : Pointer; end; procedure CDeclExceptHandler; const cUnwindInProgress = 6; asm { -> [ESP+ 4] excPtr: PExceptionRecord } { [ESP+ 8] errPtr: PExcFrame } { [ESP+12] ctxPtr: Pointer } { [ESP+16] dspPtr: Pointer } { <- EAX return value - always one } MOV EAX,[ESP+4] TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress JNE @@exit MOV EAX,[ESP+8] CALL System.@FreeMem @@exit: MOV EAX,1 end; // PreCDeclCall sets up the exception handler, and saves the return value procedure PreCDeclCall; asm { -> [ESP+ 4] ReturnAddress: Pointer } MOV EAX,TYPE TCDeclReturnInfo CALL System.@GetMem XOR EDX,EDX MOV ECX,FS:[EDX] MOV [EAX].TCDeclReturnInfo.PreviousExceptReg,ECX MOV [EAX].TCDeclReturnInfo.Handler,OFFSET CDeclExceptHandler MOV [EAX].TCDeclReturnInfo.EBPContents,EBP MOV ECX,[ESP+4] MOV [EAX].TCDeclReturnInfo.ReturnAddress,ECX MOV FS:[EDX],EAX RET 4 end; // PostCDeclCall tears down the exception handler, and returns to the caller procedure PostCDeclCall; asm { -> EAX return value of the method } { <- EAX the same return value } PUSH EAX XOR EDX,EDX MOV EAX,FS:[EDX] MOV ECX,[EAX].TCDeclReturnInfo.ReturnAddress MOV [ESP+4],ECX MOV ECX,[EAX].TCDeclReturnInfo.PreviousExceptReg MOV FS:[EDX],ECX CALL System.@FreeMem POP EAX end; var GlobObjAddress : Pointer; // Sample of a procedure which will be generated at runtime // (GlobObjAddress and TMyClass.SomeCDecl will then be hard-coded) procedure ProcForCDecl; asm CALL PreCDeclCall PUSH GlobObjAddress CALL TMyClass.SomeCDecl JMP PostCDeclCall end;
Il semble que Windows ne soit pas très d'accord avec l'idée d'un enregistrement EXCEPTION_REGISTRATION situé en dehors de la pile (ici dans le tas, alloué par le CALL System.@GetMem). Car le code ne parvient jamais dans ma CDeclExceptHandler en cas d'exception : j'ai directement droit à une erreur système.
Et de fait, dans cet article, vers la fin, l'auteur indique que l'OS vérifie que cet enregistrement est bien dans la pile
Auriez-vous une idée de comment je pourrais m'y prendre ?
d'avance
(j'espère que je suis assez clair dans mes explications )
Partager