mirror of
https://github.com/geode-sdk/geode.git
synced 2024-12-04 05:01:06 -05:00
162 lines
5.3 KiB
C++
162 lines
5.3 KiB
C++
#include "platform_macro.h"
|
|
#if defined(TARGET_ARCH_X64)
|
|
|
|
#include "InstructionRelocation/x64/X64InstructionRelocation.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "dobby_internal.h"
|
|
|
|
#include "InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h"
|
|
|
|
#include "core/arch/x64/registers-x64.h"
|
|
#include "core/modules/assembler/assembler-x64.h"
|
|
#include "core/modules/codegen/codegen-x64.h"
|
|
|
|
using namespace zz::x64;
|
|
|
|
static int GenRelocateCodeFixed(void *buffer, AssemblyCodeChunk *origin, AssemblyCodeChunk *relocated) {
|
|
TurboAssembler turbo_assembler_(0);
|
|
// Set fixed executable code chunk address
|
|
turbo_assembler_.SetRealizedAddress((void *)relocated->raw_instruction_start());
|
|
#define _ turbo_assembler_.
|
|
#define __ turbo_assembler_.GetCodeBuffer()->
|
|
|
|
addr64_t curr_orig_ip = origin->raw_instruction_start();
|
|
addr64_t curr_relo_ip = relocated->raw_instruction_start();
|
|
|
|
addr_t buffer_cursor = (addr_t)buffer;
|
|
|
|
x86_options_t conf = {0};
|
|
conf.mode = 64;
|
|
|
|
int predefined_relocate_size = origin->raw_instruction_size();
|
|
|
|
while ((buffer_cursor < ((addr_t)buffer + predefined_relocate_size))) {
|
|
int last_relo_offset = turbo_assembler_.GetCodeBuffer()->getSize();
|
|
|
|
x86_insn_decode_t insn = {0};
|
|
memset(&insn, 0, sizeof(insn));
|
|
// decode x86 insn
|
|
x86_insn_decode(&insn, (uint8_t *)buffer_cursor, &conf);
|
|
|
|
if (insn.primary_opcode >= 0x70 && insn.primary_opcode <= 0x7F) { // jc rel8
|
|
DLOG(0, "[x86 relo] jc rel8, %p", buffer_cursor);
|
|
|
|
int8_t orig_offset = insn.immediate;
|
|
int new_offset = (int)(curr_orig_ip + orig_offset - curr_relo_ip);
|
|
uint8_t opcode = 0x80 | (insn.primary_opcode & 0x0f);
|
|
|
|
__ Emit8(0x0F);
|
|
__ Emit8(opcode);
|
|
__ Emit32(new_offset);
|
|
} else if (insn.primary_opcode == 0xEB) { // jmp rel8
|
|
DLOG(0, "[x86 relo] jmp rel8, %p", buffer_cursor);
|
|
|
|
int8_t orig_offset = insn.immediate;
|
|
int8_t new_offset = (int8_t)(curr_orig_ip + orig_offset - curr_relo_ip);
|
|
|
|
__ Emit8(0xE9);
|
|
__ Emit32(new_offset);
|
|
} else if ((insn.flags & X86_INSN_DECODE_FLAG_IP_RELATIVE) && (insn.operands[1].mem.base == RIP)) { // RIP
|
|
DLOG(0, "[x86 relo] rip, %p", buffer_cursor);
|
|
|
|
// dword orig_disp = *(dword *)(buffer_cursor + insn.operands[1].mem.disp);
|
|
dword orig_disp = insn.operands[1].mem.disp;
|
|
dword new_disp = (dword)(curr_orig_ip + orig_disp - curr_relo_ip);
|
|
|
|
__ EmitBuffer((void *)buffer_cursor, insn.displacement_offset);
|
|
__ Emit32(new_disp);
|
|
if (insn.immediate_offset) {
|
|
__ EmitBuffer((void *)(buffer_cursor + insn.immediate_offset), insn.length - insn.immediate_offset);
|
|
}
|
|
} else if (insn.primary_opcode == 0xE8 || insn.primary_opcode == 0xE9) { // call or jmp rel32
|
|
DLOG(0, "[x86 relo] jmp or call rel32, %p", buffer_cursor);
|
|
|
|
dword orig_offset = insn.immediate;
|
|
dword offset = (dword)(curr_orig_ip + orig_offset - curr_relo_ip);
|
|
|
|
__ EmitBuffer((void *)buffer_cursor, insn.immediate_offset);
|
|
__ Emit32(offset);
|
|
} else if (insn.primary_opcode >= 0xE0 && insn.primary_opcode <= 0xE2) { // LOOPNZ/LOOPZ/LOOP/JECXZ
|
|
// LOOP/LOOPcc
|
|
UNIMPLEMENTED();
|
|
} else if (insn.primary_opcode == 0xE3) {
|
|
// JCXZ JCEXZ JCRXZ
|
|
UNIMPLEMENTED();
|
|
} else {
|
|
// Emit the origin instrution
|
|
__ EmitBuffer((void *)buffer_cursor, insn.length);
|
|
}
|
|
|
|
// go next
|
|
curr_orig_ip += insn.length;
|
|
buffer_cursor += insn.length;
|
|
|
|
#if 0
|
|
{
|
|
// 1 orignal instrution => ? relocated instruction
|
|
int relo_offset = turbo_assembler_.GetCodeBuffer()->getSize();
|
|
int relo_len = relo_offset - last_relo_offset;
|
|
curr_relo_ip += relo_len;
|
|
}
|
|
#endif
|
|
curr_relo_ip = relocated->raw_instruction_start() + turbo_assembler_.ip_offset();
|
|
}
|
|
|
|
// jmp to the origin rest instructions
|
|
CodeGen codegen(&turbo_assembler_);
|
|
// TODO: 6 == jmp [RIP + disp32] instruction size
|
|
addr64_t stub_addr = curr_relo_ip + 6;
|
|
codegen.JmpNearIndirect(stub_addr);
|
|
turbo_assembler_.GetCodeBuffer()->Emit64(curr_orig_ip);
|
|
|
|
// update origin
|
|
int new_origin_len = curr_orig_ip - origin->raw_instruction_start();
|
|
origin->re_init_region_range(origin->raw_instruction_start(), new_origin_len);
|
|
|
|
int relo_len = turbo_assembler_.GetCodeBuffer()->getSize();
|
|
if (relo_len > relocated->raw_instruction_size()) {
|
|
DLOG(0, "pre-alloc code chunk not enough");
|
|
return RT_FAILED;
|
|
}
|
|
|
|
// Generate executable code
|
|
{
|
|
AssemblyCodeChunk *code = NULL;
|
|
code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_);
|
|
delete code;
|
|
}
|
|
|
|
return RT_SUCCESS;
|
|
}
|
|
|
|
void GenRelocateCodeAndBranch(void *buffer, AssemblyCodeChunk *origin, AssemblyCodeChunk *relocated) {
|
|
// pre-alloc code chunk
|
|
AssemblyCodeChunk *cchunk = NULL;
|
|
|
|
int relo_code_chunk_size = 32;
|
|
const int chunk_size_step = 16;
|
|
|
|
x64_try_again:
|
|
if (relocated->raw_instruction_start() == 0) {
|
|
cchunk = MemoryArena::AllocateCodeChunk(relo_code_chunk_size);
|
|
if (cchunk == nullptr) {
|
|
return;
|
|
}
|
|
relocated->re_init_region_range((addr_t)cchunk->address, (int)cchunk->length);
|
|
}
|
|
|
|
int ret = GenRelocateCodeFixed(buffer, origin, relocated);
|
|
if (ret != RT_SUCCESS) {
|
|
// free the cchunk
|
|
MemoryArena::Destroy(cchunk);
|
|
|
|
relo_code_chunk_size += chunk_size_step;
|
|
relocated->re_init_region_range(0, 0);
|
|
|
|
goto x64_try_again;
|
|
}
|
|
}
|
|
|
|
#endif
|