mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-30 11:17:08 -05:00
318 lines
8.9 KiB
C++
318 lines
8.9 KiB
C++
#include "platform_macro.h"
|
|
#if defined(TARGET_ARCH_ARM64)
|
|
|
|
#include "InstructionRelocation/arm64/ARM64InstructionRelocation.h"
|
|
|
|
#include "dobby_internal.h"
|
|
|
|
#include "core/arch/arm64/registers-arm64.h"
|
|
#include "core/modules/assembler/assembler-arm64.h"
|
|
#include "core/modules/codegen/codegen-arm64.h"
|
|
|
|
using namespace zz::arm64;
|
|
|
|
// Compare and branch.
|
|
enum CompareBranchOp {
|
|
CompareBranchFixed = 0x34000000,
|
|
CompareBranchFixedMask = 0x7E000000,
|
|
CompareBranchMask = 0xFF000000,
|
|
};
|
|
|
|
// Conditional branch.
|
|
enum ConditionalBranchOp {
|
|
ConditionalBranchFixed = 0x54000000,
|
|
ConditionalBranchFixedMask = 0xFE000000,
|
|
ConditionalBranchMask = 0xFF000010,
|
|
};
|
|
|
|
// Test and branch.
|
|
enum TestBranchOp {
|
|
TestBranchFixed = 0x36000000,
|
|
TestBranchFixedMask = 0x7E000000,
|
|
TestBranchMask = 0x7F000000,
|
|
TBZ = TestBranchFixed | 0x00000000,
|
|
TBNZ = TestBranchFixed | 0x01000000
|
|
};
|
|
|
|
static inline int64_t SignExtend(unsigned long x, int M, int N) {
|
|
#if 0
|
|
char sign_bit = bit(x, M - 1);
|
|
unsigned long sign_mask = 0 - sign_bit;
|
|
x |= ((sign_mask >> M) << M);
|
|
#else
|
|
x = (long)(x << (N - M)) >> (N - M);
|
|
#endif
|
|
return x;
|
|
}
|
|
|
|
#if 0
|
|
static inline unsigned long set_bit(obj, st, unsigned long bit) {
|
|
return (((~(1 << st)) & obj) | (bit << st));
|
|
}
|
|
static inline unsigned long set_bits(obj, st, fn, unsigned long bits) {
|
|
return (((~(submask(fn - st) << st)) & obj) | (bits << st));
|
|
}
|
|
#endif
|
|
|
|
static inline int64_t decode_imm14_offset(uint32_t instr) {
|
|
int64_t offset;
|
|
{
|
|
int64_t imm19 = bits(instr, 5, 18);
|
|
offset = (imm19 << 2);
|
|
}
|
|
offset = SignExtend(offset, 2 + 14, 64);
|
|
return offset;
|
|
}
|
|
|
|
static inline int64_t decode_imm19_offset(uint32_t instr) {
|
|
int64_t offset;
|
|
{
|
|
int64_t imm19 = bits(instr, 5, 23);
|
|
offset = (imm19 << 2);
|
|
}
|
|
offset = SignExtend(offset, 2 + 19, 64);
|
|
return offset;
|
|
}
|
|
|
|
static inline int64_t decode_imm26_offset(uint32_t instr) {
|
|
int64_t offset;
|
|
{
|
|
int64_t imm26 = bits(instr, 0, 25);
|
|
offset = (imm26 << 2);
|
|
}
|
|
offset = SignExtend(offset, 2 + 26, 64);
|
|
return offset;
|
|
}
|
|
|
|
static inline int64_t decode_immhi_immlo_offset(uint32_t instr) {
|
|
typedef uint32_t instr_t;
|
|
struct {
|
|
instr_t Rd : 5; // Destination register
|
|
instr_t immhi : 19; // 19-bit upper immediate
|
|
instr_t dummy_0 : 5; // Must be 10000 == 0x10
|
|
instr_t immlo : 2; // 2-bit lower immediate
|
|
instr_t op : 1; // 0 = ADR, 1 = ADRP
|
|
} instr_decode;
|
|
|
|
*(instr_t *)&instr_decode = instr;
|
|
|
|
int64_t imm = instr_decode.immlo + (instr_decode.immhi << 2);
|
|
imm = SignExtend(imm, 2 + 19, 64);
|
|
return imm;
|
|
}
|
|
|
|
static inline int64_t decode_immhi_immlo_zero12_offset(uint32_t instr) {
|
|
int64_t imm = decode_immhi_immlo_offset(instr);
|
|
imm = imm << 12;
|
|
return imm;
|
|
}
|
|
|
|
static inline int decode_rt(uint32_t instr) {
|
|
return bits(instr, 0, 4);
|
|
}
|
|
|
|
static inline int decode_rd(uint32_t instr) {
|
|
return bits(instr, 0, 4);
|
|
}
|
|
|
|
#if defined(DOBBY_DEBUG)
|
|
#define debug_nop() _ nop()
|
|
#else
|
|
#define debug_nop()
|
|
#endif
|
|
|
|
void GenRelocateCodeAndBranch(void *buffer, AssemblyCodeChunk *origin, AssemblyCodeChunk *relocated) {
|
|
TurboAssembler turbo_assembler_(0);
|
|
#define _ turbo_assembler_.
|
|
|
|
uint64_t curr_orig_pc = origin->raw_instruction_start();
|
|
uint64_t curr_relo_pc = relocated->raw_instruction_start();
|
|
|
|
addr_t buffer_cursor = (addr_t)buffer;
|
|
arm64_inst_t instr = *(arm64_inst_t *)buffer_cursor;
|
|
|
|
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();
|
|
|
|
if ((instr & LoadRegLiteralFixedMask) == LoadRegLiteralFixed) { // ldr x0, #16
|
|
int rt = decode_rt(instr);
|
|
char opc = bits(instr, 30, 31);
|
|
addr64_t memory_address = decode_imm19_offset(instr) + curr_orig_pc;
|
|
|
|
#define MEM(reg, offset) MemOperand(reg, offset)
|
|
debug_nop(); // for debug
|
|
{
|
|
_ Mov(TMP_REG_0, memory_address); // should we replace with `Ldr` to set X17 ?
|
|
if (opc == 0b00)
|
|
_ ldr(W(rt), MEM(TMP_REG_0, 0));
|
|
else if (opc == 0b01)
|
|
_ ldr(X(rt), MEM(TMP_REG_0, 0));
|
|
else {
|
|
UNIMPLEMENTED();
|
|
}
|
|
}
|
|
debug_nop();
|
|
} else if ((instr & PCRelAddressingFixedMask) == PCRelAddressingFixed) {
|
|
int rd = decode_rd(instr);
|
|
|
|
int64_t imm = 0;
|
|
addr64_t runtime_address = 0;
|
|
if ((instr & PCRelAddressingMask) == ADR) {
|
|
imm = decode_immhi_immlo_offset(instr);
|
|
runtime_address = curr_orig_pc + imm;
|
|
} else {
|
|
imm = decode_immhi_immlo_zero12_offset(instr);
|
|
runtime_address = ALIGN_FLOOR(curr_orig_pc, (1 << 12)) + imm;
|
|
}
|
|
|
|
/* output Mov */
|
|
debug_nop();
|
|
{
|
|
_ Mov(X(rd), runtime_address); // should we replace with `Ldr` to set X17 ?
|
|
}
|
|
debug_nop();
|
|
|
|
} else if ((instr & UnconditionalBranchFixedMask) == UnconditionalBranchFixed) { // b xxx
|
|
addr_t branch_address = decode_imm26_offset(instr) + curr_orig_pc;
|
|
RelocLabelEntry *branchAddressLabel = new RelocLabelEntry(branch_address);
|
|
_ AppendRelocLabelEntry(branchAddressLabel);
|
|
|
|
debug_nop();
|
|
{
|
|
_ Ldr(TMP_REG_0, branchAddressLabel); // should we replace with `Mov` to set X17 ?
|
|
if ((instr & UnconditionalBranchMask) == BL) {
|
|
_ blr(TMP_REG_0);
|
|
} else {
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
debug_nop();
|
|
} else if ((instr & TestBranchFixedMask) == TestBranchFixed) { // tbz, tbnz
|
|
addr64_t branch_address = decode_imm14_offset(instr) + curr_orig_pc;
|
|
RelocLabelEntry *branchAddressLabel = new RelocLabelEntry(branch_address);
|
|
_ AppendRelocLabelEntry(branchAddressLabel);
|
|
|
|
arm64_inst_t branch_instr = instr;
|
|
|
|
char op = bit(instr, 24);
|
|
op = op ^ 1;
|
|
set_bit(branch_instr, 24, op);
|
|
|
|
int64_t offset = 4 * 3; // branch_instr; ldr x17, #label; br x17
|
|
uint32_t imm14 = offset >> 2;
|
|
set_bits(branch_instr, 5, 18, imm14);
|
|
|
|
debug_nop();
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, branchAddressLabel); // should we replace with `Mov` to set X17 ?
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
debug_nop();
|
|
|
|
} else if ((instr & CompareBranchFixedMask) == CompareBranchFixed) { // cbz cbnz
|
|
addr64_t branch_address = decode_imm19_offset(instr) + curr_orig_pc;
|
|
|
|
arm64_inst_t branch_instr = instr;
|
|
|
|
char op = bit(instr, 24);
|
|
op = op ^ 1;
|
|
set_bit(branch_instr, 24, op);
|
|
|
|
int64_t offset = 4 * 3;
|
|
uint32_t imm19 = offset >> 2;
|
|
set_bits(branch_instr, 5, 23, imm19);
|
|
|
|
RelocLabelEntry *branchAddressLabel = new RelocLabelEntry(branch_address);
|
|
_ AppendRelocLabelEntry(branchAddressLabel);
|
|
|
|
debug_nop();
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, branchAddressLabel); // should we replace with `Mov` to set X17 ?
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
debug_nop();
|
|
} else if ((instr & ConditionalBranchFixedMask) == ConditionalBranchFixed) { // b.cond
|
|
addr64_t branch_address = decode_imm19_offset(instr) + curr_orig_pc;
|
|
|
|
arm64_inst_t branch_instr = instr;
|
|
|
|
char cond = bits(instr, 0, 3);
|
|
cond = cond ^ 1;
|
|
set_bits(branch_instr, 0, 3, cond);
|
|
|
|
int64_t offset = 4 * 3;
|
|
uint32_t imm19 = offset >> 2;
|
|
set_bits(branch_instr, 5, 23, imm19);
|
|
|
|
RelocLabelEntry *branchAddressLabel = new RelocLabelEntry(branch_address);
|
|
_ AppendRelocLabelEntry(branchAddressLabel);
|
|
|
|
debug_nop();
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, branchAddressLabel); // should we replace with `Mov` to set X17 ?
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
debug_nop();
|
|
} else {
|
|
// origin write the instruction bytes
|
|
_ Emit(instr);
|
|
}
|
|
|
|
// Move to next instruction
|
|
curr_orig_pc += 4;
|
|
buffer_cursor += 4;
|
|
|
|
#if 0
|
|
{
|
|
// 1 orignal instrution => ? relocated instruction
|
|
int relo_offset = turbo_assembler_.GetCodeBuffer()->getSize();
|
|
int relo_len = relo_offset - last_relo_offset;
|
|
curr_relo_pc += relo_len;
|
|
}
|
|
#endif
|
|
|
|
curr_relo_pc = relocated->raw_instruction_start() + turbo_assembler_.pc_offset();
|
|
|
|
instr = *(arm64_inst_t *)buffer_cursor;
|
|
}
|
|
|
|
#if 0
|
|
// check branch in relocate-code range
|
|
{
|
|
for (size_t i = 0; i < data_labels->getCount(); i++) {
|
|
RelocLabelEntry *pseudoLabel = (RelocLabelEntry *)data_labels->getObject(i);
|
|
if (pseudoLabel->address == curr_orig_pc) {
|
|
FATAL("label(%p) in relo code %p, please enable b-xxx branch plugin.", pseudoLabel->address, curr_orig_pc);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// TODO: if last instr is unlink branch, skip
|
|
// Branch to the rest of instructions
|
|
CodeGen codegen(&turbo_assembler_);
|
|
codegen.LiteralLdrBranch(curr_orig_pc);
|
|
|
|
_ RelocBind();
|
|
|
|
// Generate executable code
|
|
{
|
|
AssemblyCodeChunk *code = NULL;
|
|
code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_);
|
|
relocated->re_init_region_range(code->raw_instruction_start(), code->raw_instruction_size());
|
|
delete code;
|
|
}
|
|
}
|
|
|
|
#endif
|