Как я могу упростить генерацию кода во время выполнения? - PullRequest
17 голосов
/ 06 марта 2012

Я работаю над программным обеспечением, которое генерирует код на ассемблере во время выполнения.Например, вот очень простая функция, которая генерирует код на ассемблере для вызова функции 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 ]

11 голосов
/ 06 марта 2012

Взгляните на asmjit . Это библиотека C ++ для генерации кода во время выполнения. Поддерживает x64 и, возможно, большинство существующих расширений (FPU, MMX, 3dNow, SSE, SSE2, SSE3, SSE4). Его интерфейс напоминает синтаксис сборки, и он правильно кодирует инструкции для вас.

2 голосов
/ 06 марта 2012

Вы можете зависеть от реального ассемблера, который сделает всю работу за вас - тот, который генерирует двоичный вывод, очевидно, является лучшим. Обратите внимание на yasm или fasm (на форумах fasm есть несколько сообщений о создании версии DLL, поэтому вам не нужно писать временный файл сборки, запускать внешний процесс и читать выходной файл обратно, но я не знаю, обновлялся ли он для более поздних версий).

Это может быть излишним, если ваши потребности относительно просты. Я бы подумал о создании класса C ++ Assembler, поддерживающего только необходимую вам мнемонику, а также некоторые вспомогательные функции, такие как GeneratePrologue, GenerateEpilogue, InstructionPointerRelativeAddress и тому подобное. Это позволит вам написать псевдосборку, а вспомогательные функции позаботятся о 32/64-битных проблемах.

0 голосов
/ 29 августа 2014

Очевидная вещь, которую нужно сделать, - это создать набор абстракций, которые представляют генерацию интересующих вас машинных инструкций, а затем составить вызовы, чтобы получить нужные режимы инструкций / адресации. Если вы генерируете широкий спектр кода, вы можете в итоге кодировать весь набор команд таким образом.

Затем, чтобы сгенерировать инструкцию MOV, вы можете написать код, который выглядит следующим образом:

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

Ты можешь сказать, что мне нравятся длинные имена. (По крайней мере, я никогда не забуду, что они делают.)

Вот некоторые кусочки генератора кода, поддерживающего это, который я реализовал в Си давным-давно. Это охватывает вид самой сложной части, которая является генерацией байтов 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);
}
0 голосов
/ 06 марта 2012

Вы можете абстрагироваться от некоторой кодировки команд, соглашения о вызовах и деталей, связанных с режимом ЦП, написав некоторые вспомогательные функции и макросы.

Вы даже можете создать небольшой ассемблер, который бы собирал псевдоасм-код численнозакодированы и содержатся в массиве в исполняемый код, например, начиная с ввода, как это:

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

Но это много работы, чтобы сделать это и сделать правильно.Для чего-то попроще, просто создайте вспомогательные функции / макросы, по сути делайте то, что вы уже сделали, но скрывайте некоторые неприятные детали от пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...