mirror of
https://github.com/0O0o0oOoO00/Alas.git
synced 2026-05-20 02:19:30 +08:00
219 lines
6.8 KiB
C++
219 lines
6.8 KiB
C++
#include "platform_detect_macro.h"
|
|
|
|
#if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)
|
|
|
|
#include "dobby/dobby_internal.h"
|
|
|
|
#include "InstructionRelocation/x86/InstructionRelocationX86.h"
|
|
#include "InstructionRelocation/x86/x86_insn_decode/x86_insn_decode.h"
|
|
#include "MemoryAllocator/NearMemoryAllocator.h"
|
|
|
|
using namespace zz::x86;
|
|
|
|
// x64 jmp absolute address
|
|
inline void codegen_x64_jmp_absolute_addr(CodeBufferBase *buffer, addr_t target) {
|
|
// jmp *(rip)
|
|
buffer->Emit8(0xFF);
|
|
buffer->Emit8(0x25); // ModR/M: 00 100 101
|
|
buffer->Emit32(0x00);
|
|
// .long target
|
|
buffer->Emit64(target);
|
|
}
|
|
|
|
// simple impl for ReloLabel
|
|
inline void emit_rel32_label(CodeBufferBase *buffer, uint32_t last_offset, addr_t curr_relo_ip, addr_t orig_dst_ip) {
|
|
addr_t curr_offset = buffer->GetBufferSize();
|
|
uint32_t relo_insn_len = curr_offset + sizeof(uint32_t) - last_offset;
|
|
addr_t relo_ip = curr_relo_ip + relo_insn_len;
|
|
int32_t new_offset = orig_dst_ip - relo_ip;
|
|
buffer->Emit32(new_offset);
|
|
}
|
|
|
|
int GenRelocateSingleX86Insn(addr_t curr_orig_ip, addr_t curr_relo_ip, uint8_t *buffer_cursor, AssemblerBase *assembler,
|
|
CodeBufferBase *code_buffer, x86_insn_decode_t &insn, int8_t mode) {
|
|
#define __ code_buffer->
|
|
|
|
int relocated_insn_len = -1;
|
|
|
|
x86_options_t conf = {0};
|
|
conf.mode = mode;
|
|
|
|
// decode x86/x64 insn
|
|
x86_insn_decode(&insn, (uint8_t *)buffer_cursor, &conf);
|
|
|
|
// x86 ip register == next instruction address
|
|
curr_orig_ip = curr_orig_ip + insn.length;
|
|
|
|
auto last_relo_offset = code_buffer->GetBufferSize();
|
|
|
|
static auto x86_insn_encode_start = 0;
|
|
static auto x86_insn_encoded_len = 0;
|
|
auto x86_insn_encode_begin = [&] { x86_insn_encode_start = code_buffer->GetBufferSize(); };
|
|
auto x86_insn_encode_end = [&] { x86_insn_encoded_len = code_buffer->GetBufferSize() - x86_insn_encode_start; };
|
|
|
|
if (insn.primary_opcode >= 0x70 && insn.primary_opcode <= 0x7F) { // jcc rel8
|
|
DEBUG_LOG("[x86 relo] %p: jc rel8", buffer_cursor);
|
|
|
|
int8_t offset = insn.immediate;
|
|
addr_t orig_dst_ip = curr_orig_ip + offset;
|
|
#if defined(TARGET_ARCH_IA32)
|
|
uint8_t opcode = 0x80 | (insn.primary_opcode & 0x0f);
|
|
|
|
x86_insn_encode_begin();
|
|
__ Emit8(0x0F);
|
|
__ Emit8(opcode);
|
|
emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip);
|
|
#else
|
|
// jcc_true stage 1
|
|
const uint8_t label_jcc_cond_true_stage2 = 2;
|
|
__ Emit8(insn.primary_opcode);
|
|
__ Emit8(label_jcc_cond_true_stage2);
|
|
|
|
// jcc_false
|
|
const uint8_t label_cond_false = 6 + 8;
|
|
__ Emit8(0xEB);
|
|
__ Emit8(label_cond_false);
|
|
|
|
// jcc_true stage 2, jmp to orig dst
|
|
codegen_x64_jmp_absolute_addr(code_buffer, orig_dst_ip);
|
|
#endif
|
|
|
|
} else if (mode == 64 && (insn.flags & X86_INSN_DECODE_FLAG_IP_RELATIVE) &&
|
|
(insn.operands[1].mem.base == RIP)) { // RIP
|
|
DEBUG_LOG("[x86 relo] %p: rip", buffer_cursor);
|
|
|
|
int32_t orig_disp = insn.operands[1].mem.disp;
|
|
addr_t orig_dst_ip = curr_orig_ip + orig_disp;
|
|
|
|
addr_t rip_insn_seq_addr = 0;
|
|
{
|
|
|
|
uint32_t jmp_near_range = (uint32_t)2 * 1024 * 1024 * 1024;
|
|
auto rip_insn_seq = (addr_t)NearMemoryAllocator::SharedAllocator()->allocateNearExecMemory(
|
|
insn.length + 6 + 8, orig_dst_ip, jmp_near_range);
|
|
|
|
rip_insn_seq_addr = rip_insn_seq;
|
|
}
|
|
|
|
// jmp *(rip) => jmp to [rip insn seq]
|
|
x86_insn_encode_begin();
|
|
__ Emit8(0xFF);
|
|
__ Emit8(0x25); // ModR/M: 00 100 101
|
|
__ Emit32(0);
|
|
__ Emit64(rip_insn_seq_addr);
|
|
x86_insn_encode_end();
|
|
|
|
{
|
|
auto rip_insn_seq_buffer = CodeBufferBase();
|
|
#define ___ rip_insn_seq_buffer.
|
|
|
|
auto rip_insn_req_ip = rip_insn_seq_addr;
|
|
rip_insn_req_ip = rip_insn_req_ip + insn.length; // next insn addr
|
|
int32_t new_disp = (int32_t)(orig_dst_ip - rip_insn_req_ip);
|
|
|
|
// keep orig insn opcode
|
|
___ EmitBuffer(buffer_cursor, insn.displacement_offset);
|
|
___ Emit32(new_disp);
|
|
// keep orig insn immediate
|
|
if (insn.immediate_offset) {
|
|
___ EmitBuffer((buffer_cursor + insn.immediate_offset), insn.length - insn.immediate_offset);
|
|
}
|
|
|
|
// jmp *(rip) => back to relo process
|
|
auto relo_next_ip = curr_relo_ip + x86_insn_encoded_len;
|
|
codegen_x64_jmp_absolute_addr(&rip_insn_seq_buffer, relo_next_ip);
|
|
|
|
DobbyCodePatch((void *)rip_insn_seq_addr, rip_insn_seq_buffer.GetBuffer(), rip_insn_seq_buffer.GetBufferSize());
|
|
}
|
|
|
|
} else if (insn.primary_opcode == 0xEB) { // jmp rel8
|
|
DEBUG_LOG("[x86 relo] %p: jmp rel8", buffer_cursor);
|
|
|
|
int8_t offset = insn.immediate;
|
|
addr_t orig_dst_ip = curr_orig_ip + offset;
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
x86_insn_encode_begin();
|
|
__ Emit8(0xE9);
|
|
emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip);
|
|
#else
|
|
// jmp *(rip)
|
|
codegen_x64_jmp_absolute_addr(code_buffer, orig_dst_ip);
|
|
#endif
|
|
} else if (insn.primary_opcode == 0xE8 || insn.primary_opcode == 0xE9) { // call or jmp rel32
|
|
DEBUG_LOG("[x86 relo] %p:jmp or call rel32", buffer_cursor);
|
|
|
|
int32_t offset = insn.immediate;
|
|
addr_t orig_dst_ip = curr_orig_ip + offset;
|
|
|
|
assert(insn.immediate_offset == 1);
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
x86_insn_encode_begin();
|
|
|
|
__ EmitBuffer(buffer_cursor, insn.immediate_offset);
|
|
emit_rel32_label(code_buffer, x86_insn_encode_start, curr_relo_ip, orig_dst_ip);
|
|
#else
|
|
__ Emit8(0xFF);
|
|
if (insn.primary_opcode == 0xE8) {
|
|
// call *(rip + 2)
|
|
__ Emit8(0x15); // ModR/M: 00 010 101
|
|
__ Emit32(2);
|
|
|
|
// jmp 8
|
|
__ Emit8(0xEB);
|
|
__ Emit8(0x08);
|
|
|
|
// dst
|
|
__ Emit64(orig_dst_ip);
|
|
} else {
|
|
// jmp *(rip)
|
|
__ Emit8(0x25); // ModR/M: 00 100 101
|
|
__ Emit32(0);
|
|
|
|
// dst
|
|
__ Emit64(orig_dst_ip);
|
|
}
|
|
#endif
|
|
} 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 {
|
|
__ EmitBuffer(buffer_cursor, insn.length);
|
|
}
|
|
|
|
// insn -> relocated insn
|
|
{
|
|
int relo_offset = code_buffer->GetBufferSize();
|
|
int relo_len = relo_offset - last_relo_offset;
|
|
DEBUG_LOG("insn -> relocated insn: %d -> %d", insn.length, relo_len);
|
|
}
|
|
return relocated_insn_len;
|
|
}
|
|
|
|
void GenRelocateCodeX86Shared(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) {
|
|
int expected_relocated_mem_size = 32;
|
|
x86_try_again:
|
|
if (!relocated->addr) {
|
|
auto relocated_mem = MemoryAllocator::SharedAllocator()->allocateExecMemory(expected_relocated_mem_size);
|
|
if (relocated_mem == nullptr) {
|
|
return;
|
|
}
|
|
relocated->reset((addr_t)relocated_mem, expected_relocated_mem_size);
|
|
}
|
|
|
|
int ret = GenRelocateCodeFixed(buffer, origin, relocated, branch);
|
|
if (ret != 0) {
|
|
const int step_size = 16;
|
|
expected_relocated_mem_size += step_size;
|
|
relocated->reset(0, 0);
|
|
|
|
goto x86_try_again;
|
|
}
|
|
}
|
|
|
|
#endif
|