JIT脚本引擎:完成链接器的核心功能

JIT脚本引擎:完成链接器的核心功能
    光能编译汇编还是不行的,因为很多东西在编译的时候不知道,典型的比如放常量那部分的指针等等。主要原因还是因为x87(指FPU部分)没有指令包含浮点立即数,所有装载浮点常数的指令都要求提供指针。所以诸如double a=1.2;之类的代码,需要将1.2预先放置在一个地方然后确定指针的位置。

    于是就遇到了一个问题,如何将编译后才知道的指针地址写进去呢?唯一的办法就是在二进制代码那里留空,然后使用一张新表记录哪些地方是需要链接的时候填充的。于是可以使用如下结构来构造一张链接符号表:
1  struct  LinkingRef
2  {
3      VInt            ID;
4      VInt            Offset;
5      VSize            Position;
6      VByte            Bits; // 0->8, 1->16, 2->32
7      VInt            Instruction;
8  };
    其中ID代表链接的对象,譬如可以将0作为常数缓冲区的指针,其他数字作为外部函数的地址等等。Offset代表一个常量偏移,可以将ID所代表的地址加上一个数字之后存放在指令缓冲区的Position偏移处。至于地址的大小使用Bits来表达(因为整数也可以如此搞)。Instruction记录的是使用这个链接单元的指令序号,用于生成错误信息。

    于是在编译之后就可以填充链接信息,然后进行链接了。这个做完以后就只剩下一个问题了。我们不仅需要常量缓冲区、还需要变量缓冲区,那么如何在常量缓冲区填充之后锁定呢(为了在试图修改的时候抛出异常)?Windows API提供了VirtualAlloc、VirtualProtect和VirtualFree来帮我们完成这项工作。为了简化操作,使用以下的类用于控制内存空间:
 1               struct  VL_AsmExecutable
 2              {
 3               private :
 4                  VPointer                FConstantBuffer;
 5                  VPointer                FVariableBuffer;
 6                  VPointer                FInstructionBuffer;
 7                  VInt                    FConstantSize;
 8                  VInt                    FVariableSize;
 9                  VInt                    FInstructionSize;
10                  VBool                    FAvailable;
11 
12               public :
13                  VL_AsmExecutable();
14                   ~ VL_AsmExecutable();
15 
16                   void                     Allocate(VInt Constant , VInt Variable , VInt Instruction);
17                   void                     Protect();
18                   void                     Release();
19                  VPointer                GetConstant();
20                  VPointer                GetVariable();
21                  VPointer                GetInstruction();
22              };

    这里是实现部分:
 1              VL_AsmExecutable::VL_AsmExecutable()
 2              {
 3                  FConstantBuffer = 0 ;
 4                  FVariableBuffer = 0 ;
 5                  FInstructionBuffer = 0 ;
 6                  FConstantSize = 0 ;
 7                  FVariableSize = 0 ;
 8                  FInstructionSize = 0 ;
 9                  FAvailable = false ;
10              }
11 
12              VL_AsmExecutable:: ~ VL_AsmExecutable()
13              {
14                  Release();
15              }
16 
17               void  VL_AsmExecutable::Allocate(VInt Constant , VInt Variable , VInt Instruction)
18              {
19                  Release();
20                  FConstantSize = Constant ? Constant: 1 ;
21                  FVariableSize = Variable ? Variable: 1 ;
22                  FInstructionSize = Instruction ? Instruction: 1 ;
23                  FConstantBuffer = VirtualAlloc( 0 ,FConstantSize,MEM_COMMIT,PAGE_READWRITE);
24                  FVariableBuffer = VirtualAlloc( 0 ,FVariableSize,MEM_COMMIT,PAGE_READWRITE);
25                  FInstructionBuffer = VirtualAlloc( 0 ,FInstructionSize,MEM_COMMIT,PAGE_READWRITE);
26                  FAvailable = true ;
27              }
28 
29               void  VL_AsmExecutable::Protect()
30              {
31                   if (FAvailable)
32                  {
33                      DWORD OldProtect = 0 ;
34                      VirtualProtect(FConstantBuffer,FConstantSize,PAGE_READONLY, & OldProtect);
35                      VirtualProtect(FInstructionBuffer,FInstructionSize,PAGE_EXECUTE_READ, & OldProtect);
36                  }
37              }
38 
39               void  VL_AsmExecutable::Release()
40              {
41                   if (FAvailable)
42                  {
43                      VirtualFree(FConstantBuffer,FConstantSize,MEM_RELEASE);
44                      VirtualFree(FVariableBuffer,FVariableSize,MEM_RELEASE);
45                      VirtualFree(FInstructionBuffer,FInstructionSize,MEM_RELEASE);
46                      
47                      FConstantBuffer = 0 ;
48                      FVariableBuffer = 0 ;
49                      FInstructionBuffer = 0 ;
50                      FConstantSize = 0 ;
51                      FVariableSize = 0 ;
52                      FInstructionSize = 0 ;
53                      FAvailable = false ;
54                  }
55              }
56 
57              VPointer VL_AsmExecutable::GetConstant()
58              {
59                   return  FConstantBuffer;
60              }
61 
62              VPointer VL_AsmExecutable::GetVariable()
63              {
64                   return  FVariableBuffer;
65              }
66 
67              VPointer VL_AsmExecutable::GetInstruction()
68              {
69                   return  FInstructionBuffer;
70              }

    到了这里就可以修改 上一篇文章列出的汇编代码了。我们将数组{10,1,2,3,4,5,6,7,8,9,10}存放进常量空间然后求和:
 1  VL_AsmCompiled *  Compile()
 2  {
 3      VL_AsmProgram Program;
 4      {
 5          VInt Params[] = { 10 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
 6          Program.ConstantBuffer.Write( & Params, sizeof (Params));
 7      }
 8      {
 9           //  XOR EAX, EAX
10          INSTRUCTION(XOR)
11          PARAM_REG_32(EAX)
12          PARAM_REG_32(EAX)
13           //  MOV EDI, #CONSTANT_BUFFER_POINTER
14          INSTRUCTION(MOV)
15          PARAM_REG_32(EDI)
16          PARAM_IMM_32( 0 )
17          LINK(VL_AsmProgram::CONSTANT_BUFFER_POINTER, 0 );
18           //  MOV ECX, [EDI]
19          INSTRUCTION(MOV)
20          PARAM_REG_32(ECX)
21          PARAM_MI_32(NONE, 1 ,EDI, 0 )
22           //  @BEGIN:
23          LABEL(BEGIN)
24               //  CMP ECX, 0
25              INSTRUCTION(CMP)
26              PARAM_REG_32(ECX)
27              PARAM_IMM_32( 0 )
28               //  JE @END
29              INSTRUCTION(JE)
30              PARAM_LABEL(END)
31               //  ADD EAX, [ECX * 4 + EDI]
32              INSTRUCTION(ADD)
33              PARAM_REG_32(EAX)
34              PARAM_MI_32(ECX, 4 ,EDI, 0 )
35               //  SUB ECX, 1
36              INSTRUCTION(SUB)
37              PARAM_REG_32(ECX)
38              PARAM_IMM_32( 1 )
39               //  JMP @BEGIN
40              INSTRUCTION(JMP)
41              PARAM_LABEL(BEGIN)
42           //  @END:
43          LABEL(END)
44           //  RET
45          INSTRUCTION(RET)
46      }
47      VL_AsmCompiled *  Compiled = Compile( & Program);
48       if (Compiled -> Errors.GetCount())
49      {
50          PrintErrors(Compiled);
51          delete Compiled;
52           return   0 ;
53      }
54       else
55      {
56           return  Compiled;
57      }
58  }

    然后使用下面的代码执行编译后的机器码:
 1  void  RunExecutable(VL_AsmExecutable *  Executable)
 2  {
 3      VPointer Buffer = Executable -> GetInstruction();
 4      VInt Output = 0 ;
 5      __asm
 6      {
 7          PUSHAD
 8          MOV EAX, dword ptr[Buffer]
 9          INT  3
10          CALL EAX
11          MOV dword ptr[Output], EAX
12          POPAD
13      }
14      GetConsole() -> Write(L " 结果:EAX= " + VUnicodeString(Output) + L " \r\n " );
15  }
16 
17  void  vlmain()
18  {
19      GetConsole() -> SetTitle(L " Vczh Library++ 2.0 Assembler " );
20      GetConsole() -> SetTestMemoryLeaks( true );
21      GetConsole() -> SetPauseOnExit( true );
22 
23      VL_AsmCompiled *  Compiled = Compile();
24       if (Compiled)
25      {
26          VL_AsmExecutable *  Executable = Link(Compiled);
27           if (Compiled -> Errors.GetCount())
28          {
29              PrintErrors(Compiled);
30          }
31           if (Executable)
32          {
33              RunExecutable(Executable);
34              delete Executable;
35          }
36          delete Compiled;
37      }
38  }

    链接器就完成了。

你可能感兴趣的:(JIT脚本引擎:完成链接器的核心功能)