随笔-341  评论-2670  文章-0  trackbacks-0
    光能编译汇编还是不行的,因为很多东西在编译的时候不知道,典型的比如放常量那部分的指针等等。主要原因还是因为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 }

    链接器就完成了。
posted on 2009-02-22 22:41 陈梓瀚(vczh) 阅读(1919) 评论(1)  编辑 收藏 引用 所属分类: JIT

评论:
# re: JIT脚本引擎:完成链接器的核心功能 2009-02-23 05:21 | Corner Zhang
这厮咋动作这么快呀!呵呵  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理