如何在运行时简化代码生成?

时间:2012-03-06 08:48:13

标签: c++ windows visual-c++ assembly x86

我正在研究一种在运行时生成汇编代码的软件。例如, 这是一个非常简单的函数,它生成用于调用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,它们都发出汇编代码,其中很多函数指针被调用作为参数传递。

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

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

4 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:2)

您可以依靠真正的汇编程序为您完成工作 - 生成二进制输出的组件显然是最好的。考虑查看yasmfasm(在fasm论坛上有关于执行DLL版本的一些帖子,因此您不必编写临时程序集文件,启动外部进程并读取输出文件,但是我不知道它是否已经为更高版本更新了。)

如果您的需求相对简单,这可能会有点过头了。我考虑做一个C ++ Assembler类,只支持你需要的助记符,以及一些辅助函数,如GeneratePrologueGenerateEpilogueInstructionPointerRelativeAddress等。这将允许您编写伪程序集,并使辅助函数处理32/64位问题。

答案 2 :(得分:0)

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

你甚至可以创建一个小的汇编程序,它将伪数据编码和数组中包含的伪asm代码组合成可运行的代码,例如:从这样的输入开始:

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

但要完成这项任务并做得很好,还有很多工作要做。对于更简单的东西,只需创建辅助函数/宏,基本上做你已经完成的事情,但隐藏用户的一些讨厌的细节。

答案 3 :(得分:0)

显而易见的事情是构建一组抽象,表示感兴趣的机器指令的元素的生成,然后组合调用以获得所需的指令/寻址模式。如果生成各种代码,最终可以用这种方式编写整个指令集。

然后要生成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);
}