Как я могу упростить создание кода во время выполнения? - программирование
Подтвердить что ты не робот

Как я могу упростить создание кода во время выполнения?

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Затем, чтобы сгенерировать инструкцию 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);
}