随笔-341  评论-2670  文章-0  trackbacks-0

    使用上一篇文章的资料,就可以使用汇编实现异常处理了。下面来看一个例子。

    这是一个汇编实现的函数,计算一个数字厨艺另一个数字得到的结果和余数。如果发生了除零错的话,使用异常处理将结果和余数都设置为0。

 1 CONSTANT
 2 VARIABLE
 3 CODE
 4 
 5 //struct{int Quotient;int Remainder;}
 6 //__stdcall
 7 //divmod(
 8 //    int Num1,
 9 //    int Num2)
10 @DIVMOD:
11   PUSH EBP               //EBP->[EBP][EIP][Num1][Num2]
12   MOV EBP, ESP
13   PUSH ECX               //ESP->[ECX] EBP->
14   SUB ESP, int32 8       //ESP->[Remainder][Quotient][ECX] EBP->
15 @DIVMOD_BEGIN_TRY:
16   PUSH EBP
17   PUSH int32 0
18   PUSH int32 @DIVMOD_END_TRY
19   PUSH int32 @DIVMOD_CATCH
20   PUSH int32 #EXCEPTION_HANDLER
21   FS PUSH int32 [0]
22   FS MOV int32 [0], ESP   //FS:[0]->ESP->[Prev][Handler][Catch][EndTry][Unwind][EBP][Remainder][Quotient][ECX] EBP->
23   XOR EDX, EDX
24   MOV EAX, int32 [EBP+8]
25   IDIV int32 [EBP+12]
26   MOV int32 [EBP-12], EDX
27   MOV int32 [EBP-8], EAX
28 @DIVMOD_END_TRY:
29   MOV ECX, int32 [ESP]
30   FS MOV int32 [0], ECX   //FS:[0]->Previous Handler
31   ADD ESP, int32 24       //ESP->[Remainder][Quotient][ECX] EBP->
32   JMP @DIVMOD_FINISH_TRY_CATCH
33 @DIVMOD_CATCH:
34   PUSH EBP
35   MOV EBP, ECX
36   XOR EAX, EAX
37   MOV int32 [EBP-12], EAX
38   MOV int32 [EBP-8], EAX
39   MOV EAX, int32 1
40   POP EBP
41   RET
42 @DIVMOD_FINISH_TRY_CATCH:
43   POP EDX                //EDX=Remainder
44   POP EAX                //EAX=Quotient
45   POP ECX
46   MOV ESP, EBP
47   POP EBP
48   RET int16 8

    注释除标明了函数的原型。这个函数接受两个DWORD参数,返回一个包含两个DWORD的struct,使用__stdcall调用方法。上面的函数在进行除法之前向FS:[0]处写入了一个异常处理节点。执行到第22行的时候,可以看到FS:[0]和ESP两个指针指向的数据的内容。于是运行到第25行的IDIV指令。如果正确的话将撤销FS:[0],错误的话不仅要撤销,还要在堆栈预留的地方([Remainder][Quotient],见第14行)写入两个0。

    函数执行结束之后,向[Remainder][Quotient]索取内容依次放入EDX和EAX,用第43、44行的两个POP实现。最后结束函数。至于异常处理函数的内容是什么呢?由于过于复杂,我使用C++实现:
 1 DWORD RunCatch(void * EstablisherFrame)
 2 {
 3     typedef DWORD (__fastcall * CatchHandler)(DWORD EBP);
 4     CatchHandler Catch=(CatchHandler)((VInt*)EstablisherFrame)[2];
 5     return Catch(((VInt*)EstablisherFrame)[5]);
 6 }
 7 
 8 DWORD RunUnwind(void * EstablisherFrame)
 9 {
10     typedef DWORD (__fastcall * UnwindHandler)(DWORD EBP);
11     UnwindHandler Unwind=(UnwindHandler)((VInt*)EstablisherFrame)[4];
12     if(Unwind)
13     {
14         return Unwind(((VInt*)EstablisherFrame)[5]);
15     }
16     else
17     {
18         return 0;
19     }
20 }
21 
22 EXCEPTION_DISPOSITION __cdecl EXCEPTION_HANDLER
23 (
24      struct _EXCEPTION_RECORD *ExceptionRecord,
25      void * EstablisherFrame,
26      struct _CONTEXT *ContextRecord,
27      void * DispatcherContext
28 )
29 {
30     //FS:[0] -> [Prev][Handler][Catch][EndTry][Unwind][EBP]
31     //          0     1        2      3       4       5
32     if(ExceptionRecord->ExceptionFlags==0)
33     {
34         if(RunCatch(EstablisherFrame))
35         {
36             RunUnwind(EstablisherFrame);
37             ContextRecord->Eip=((VInt*)EstablisherFrame)[3];
38             ContextRecord->Ebp=((VInt*)EstablisherFrame)[5];
39             ContextRecord->Esp=(DWORD)EstablisherFrame;
40             return ExceptionContinueExecution;
41         }
42         else
43         {
44             return ExceptionContinueSearch;
45         }
46     }
47     else if(ExceptionRecord->ExceptionFlags & 2)
48     {
49         RunUnwind(EstablisherFrame);
50         return ExceptionContinueSearch;
51     }
52     else
53     {
54         return ExceptionContinueSearch;
55     }
56 }

    于是我们分别用10/3和10/0两组数字调用该函数:
 1 struct ASM_RESULT
 2 {
 3     VInt Quotient;
 4     VInt Remainder;
 5 };
 6 
 7 typedef ASM_RESULT(__stdcall*ASM_DIVMOD)(VInt Num1 , VInt Num2);
 8 
 9 void RunExecutable(VL_AsmProgram* Program , VL_AsmCompiled* Compiled , VL_AsmExecutable* Executable)
10 {
11     VInt Offset=(VInt)Compiled->LabelOffsets[Program->LabelNames.IndexOf(L"@DIVMOD")];
12     ASM_DIVMOD Summer=(ASM_DIVMOD)((VInt)Executable->GetInstruction()+Offset);
13     {
14         ASM_RESULT Result=Summer(10,3);
15         GetConsole()->Write(L"Quotient = "+VUnicodeString(Result.Quotient)+L"\r\n");
16         GetConsole()->Write(L"Remainder = "+VUnicodeString(Result.Remainder)+L"\r\n");
17     }
18     {
19         ASM_RESULT Result=Summer(10,0);
20         GetConsole()->Write(L"Quotient = "+VUnicodeString(Result.Quotient)+L"\r\n");
21         GetConsole()->Write(L"Remainder = "+VUnicodeString(Result.Remainder)+L"\r\n");
22     }
23 }

    得到如下结果:

 


    大功告成!
posted on 2009-03-03 00:20 陈梓瀚(vczh) 阅读(2319) 评论(1)  编辑 收藏 引用 所属分类: JIT

评论:
# re: JIT脚本引擎:使用汇编实现__try和__catch 2009-03-03 05:43 | lnn
congratulations!  回复  更多评论