我怎样才能在运行时简化代码生成?

我正在研究一个在运行时生成汇编代码的软件。 例如,下面是一个非常简单的函数,它生成用于调用GetCurrentProcess函数(用于Win64 ABI)的汇编代码:

 void genGetCurrentProcess( char *codePtr, FARPROC addressForGetCurrentProcessFunction ) { #ifdef _WIN64 // mov rax, addressForGetCurrentProcessFunction *codePtr++ = 0x48 *codePtr++ = 0xB8; *((FARPROC *)codePtr)++ = addressForGetCurrentProcessFunction; // call rax *codePtr++ = 0xFF; *codePtr++ = 0xD0; #else // mov eax, addressForGetCurrentProcessfunction *codePtr++ = 0xB8; *((FARPROC *)codePtr)++ = addressForGetCurrentProcessFunction; // call eax *codePtr++ = 0xFF; *codePtr++ = 0xD0; #endif } 

通常我会使用内联汇编程序,但唉 – 这似乎不可能与64位MSVC编译器了。 当我在这里 – 这个代码应该与MSVC6 MSVC10和MinGW。 还有更多像genGetCurrentProcess这样的genGetCurrentProcess ,它们都发出汇编代码,其中很多函数指针被作为parameter passing。

令人烦恼的是,修改这个代码很容易出错,我们必须手动处理ABI特定的事情(例如,在调用寄存器溢出的函数之前预留32个字节的堆栈空间)。

所以我的问题是 – 我可以简化这个代码在运行时生成汇编代码? 我的希望是,我可以以某种方式直接编写汇编代码(可能在一个外部文件,然后使用ml / ml64组装),但是我不清楚如果汇编代码中的某些字节只在运行时(例如上例中的addressForGetcurrentProcessFunction值)。 也许有可能组装一些代码,但是将“标签”分配给代码中的特定位置,以便我可以在运行时轻松修改代码,然后将其复制到我的缓冲区中?

看看asmjit 。 它是用于运行时代码生成的C ++库。 支持x64以及大多数现有的扩展(FPU,MMX,3dNow,SSE,SSE2,SSE3,SSE4)。 它的接口类似于汇编语法,它为您正确编码指令。

你可以依靠一个真正的汇编程序来为你做这个工作 – 产生二进制输出的显然是最好的。 考虑看看yasm或fasm (在fasm论坛上有一些关于做DLL版本的帖子,所以你不必编写临时的程序集文件,启动外部进程,并读取输出文件,但我不知道是否已经更新为更高版本)。

但是,如果你的需求相对简单,这可能是过度的。 我会考虑做一个C ++汇编程序类,只支持你需要的助记符,以及一些辅助功能,如GeneratePrologueGenerateEpilogueInstructionPointerRelativeAddress等。 这将允许您编写伪汇编,并有助手功能照顾32/64位问题。

你可以通过编写一些辅助函数和宏来抽象出一些指令编码,调用约定和CPU模式相关的细节。

你甚至可以创建一个小的汇编程序,将数组编码的数字编码的伪汇编集成到一个可运行的代码中,例如像这样的输入开始:

 UINT32 blah[] = { mov_, ebx_, dwordPtr_, edi_, plus_, eax_, times8_, plus_, const_, 0xFEDCBA98, call_, dwordPtr_, ebx_, }; 

但是要完成这项工作并做对了很多工作。 对于更简单的事情,只需创建助手函数/宏,基本上做你已经做的,但隐藏用户的一些讨厌的细节。

显而易见的事情是建立一组代表感兴趣的机器指令元素的抽象,然后组合调用来获得你想要的指令/寻址模式。 如果您生成各种各样的代码,您可以以这种方式编码整个指令集。

然后生成一个MOV指令,你可以编写如下的代码:

 ObjectCodeEmitMovRegister32ScaledRegister32OffsetRegister32(EAX,EDX,4,-LowerBound*4,ESP); 

你可以告诉我喜欢长名字。 (至少我永远不会忘记他们做什么。)

这里有一些代码生成器支持我很久以前在C中实现的一些代码生成器。 这包括最难的部分,这是MOD和SIB字节的产生。 遵循这种风格,可以实现尽可能多的指令集。 这个例子只适用于x32,因此OP将不得不进行相应的扩展和修改。 MOV指令生成器的定义在最后。

 #define Register32T enum Register32Type enum Register32Type {EAX=0,ECX=1,EDX=2,EBX=3,ESP=4,EBP=5,ESI=6,EDI=7}; inline byte ObjectCodeEmitModRM32Register32(Register32T Register32,Register32T BaseRegister32) // Send ModRM32Bytes for register-register mode to object file { byte ModRM32Byte=0xC0+Register32*0x8+BaseRegister32; ObjectCodeEmitByte(ModRM32Byte); return ModRM32Byte; } inline byte ObjectCodeEmitModRM32Direct(Register32T Register32) // Send ModRM32Bytes for direct address mode to object file { byte ModRM32Byte=Register32*0x8+0x05; ObjectCodeEmitByte(ModRM32Byte); return ModRM32Byte; } inline void ObjectCodeEmitSIB(Register32T ScaledRegister32, natural Scale, Register32T BaseRegister32) // send SIB byte to object file // Note: Use ESP for ScaledRegister32 to disable scaling; only useful when using ESP for BASE. { if (ScaledRegister32==ESP && BaseRegister32!=ESP) CompilerFault(31); if (Scale==1) ObjectCodeEmitByte((byte)(0x00+ScaledRegister32*0x8+BaseRegister32)); else if (Scale==2) ObjectCodeEmitByte((byte)(0x40+ScaledRegister32*0x8+BaseRegister32)); else if (Scale==4) ObjectCodeEmitByte((byte)(0x80+ScaledRegister32*0x8+BaseRegister32)); else if (Scale==8) ObjectCodeEmitByte((byte)(0xC0+ScaledRegister32*0x8+BaseRegister32)); else CompilerFault(32); } inline byte ObjectCodeEmitModRM32OffsetRegister32(Register32T Register32, integer Offset, Register32T BaseRegister32) // Send ModRM32Bytes for indexed address mode to object file // Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization { byte ModRM32Byte; if (Offset==0 && BaseRegister32!=EBP) { ModRM32Byte=0x00+Register32*0x8+BaseRegister32; ObjectCodeEmitByte(ModRM32Byte); if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); } else if (Offset>=-128 && Offset<=127) { ModRM32Byte=0x40+Register32*0x8+BaseRegister32; ObjectCodeEmitByte(ModRM32Byte); if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); ObjectCodeEmitByte((byte)Offset); } else { // large offset ModRM32Byte=0x80+Register32*0x8+BaseRegister32; ObjectCodeEmitByte(ModRM32Byte); if (BaseRegister32==ESP) ObjectCodeEmitSIB(ESP,1,ESP); ObjectCodeEmitDword(Offset); } return ModRM32Byte; } inline byte ObjectCodeEmitModRM32OffsetScaledRegister32(Register32T Register32, integer Offset, Register32T ScaledRegister32, natural Scale) // Send ModRM32Bytes for indexing by a scaled register with no base register to object file // Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization { byte ModRM32Byte=0x00+Register32*0x8+ESP; ObjectCodeEmitByte(ModRM32Byte); // MOD=00 --> SIB does disp32[index] ObjectCodeEmitSIB(ScaledRegister32,Scale,EBP); ObjectCodeEmitDword(Offset); return ModRM32Byte; } inline byte ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32(Register32T Register32, Register32T ScaledRegister32, natural Scale, integer Offset, Register32T BaseRegister32) // Send ModRM32Bytes for indexed address mode to object file // Returns 1st byte of ModRM32 for possible use in EmittedPushRM32 peephole optimization // If Scale==0, leave scale and scaled register out of the computation { byte ModRM32Byte; if (Scale==0) ObjectCodeEmitModRM32OffsetRegister32(Register32,Offset,BaseRegister32); else if (Offset==0 && BaseRegister32!=EBP) { ModRM32Byte=0x00+Register32*0x8+ESP; ObjectCodeEmitByte(ModRM32Byte); ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); } else if (Offset>=-128 && Offset<=127) { ModRM32Byte=0x40+Register32*0x8+ESP; ObjectCodeEmitByte(ModRM32Byte); ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); ObjectCodeEmitByte((byte)Offset); } else { // large offset ModRM32Byte=0x80+Register32*0x8+ESP; ObjectCodeEmitByte(ModRM32Byte); ObjectCodeEmitSIB(ScaledRegister32,Scale,BaseRegister32); ObjectCodeEmitDword(Offset); } return ModRM32Byte; } inline void ObjectCodeEmitLeaRegister32OffsetRegister32ScaledPlusBase32( Register32T Register32Destination, integer Offset, Register32T Register32Source, natural Scale, // 1,2,4 or 8 Register32T Base) // send "LEA Register32,offset[Register32*Scale+Base]" to object file { ObjectCodeEmitLeaOpcode(); ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32( Register32Destination,Register32Source,Scale,Offset,Base); } inline void ObjectCodeEmitMovRegister32ScaledRegister32OffsetRegister32(Register32T DestinationRegister32, Register32T ScaledRegister32, natural Scale, integer Offset, Register32T BaseRegister32) // Emit Mov R32 using scaled index addressing { ObjectCodeEmitMovRegister32Opcode(); ObjectCodeEmitModRM32ScaledRegister32OffsetRegister32(DestinationRegister32, ScaledRegister32, Scale, Offset, BaseRegister32); }