kernel: Polish trampoline injection

This commit is contained in:
sunho 2021-05-19 10:38:57 +09:00 committed by Sunho Kim
parent 7d6d79c399
commit be53a21f2f
7 changed files with 57 additions and 38 deletions

View File

@ -38,6 +38,7 @@ struct CPUInterface {
virtual CPUContext save_context() = 0;
virtual void load_context(CPUContext context) = 0;
virtual void invalidate_jit_cache(Address start, size_t length) {}
virtual bool is_thumb_mode() = 0;
virtual int step() = 0;

View File

@ -183,7 +183,6 @@ public:
case Dynarmic::A32::Exception::Breakpoint: {
cpu->did_break = true;
cpu->jit->HaltExecution();
cpu->jit->ClearCache();
if (cpu->is_thumb_mode())
cpu->set_pc(pc | 1);
else

View File

@ -78,7 +78,7 @@ void UnicornCPU::intr_hook(uc_engine *uc, uint32_t intno, void *user_data) {
uint32_t svc_instruction = 0;
const auto err = uc_mem_read(uc, svc_address, &svc_instruction, sizeof(svc_instruction));
assert(err == UC_ERR_OK);
const uint32_t imm = svc_instruction & 0xffffff;
const uint32_t imm = state.is_thumb_mode() ? (svc_instruction & 0xff0000) >> 16 : svc_instruction & 0xffffff;
state.parent->protocol->call_svc(*state.parent, imm, pc, get_thread_id(*state.parent));
} else if (intno == INT_BKPT) {
state.stop();

View File

@ -30,7 +30,7 @@ CPUProtocol::~CPUProtocol() {
void CPUProtocol::call_svc(CPUState &cpu, uint32_t svc, Address pc, SceUID thread_id) {
if (svc == TRAMPOLINE_SVC) {
Trampoline *tr = *Ptr<Trampoline *>(pc + 4).get(*mem);
Trampoline *tr = *Ptr<Trampoline *>(pc).get(*mem);
tr->callback(*kernel, cpu, *mem, tr->lr);
return;
}

View File

@ -2,11 +2,12 @@
#include <kernel/state.h>
#include <util/align.h>
#include <util/arm.h>
#include <util/log.h>
constexpr unsigned char THUMB_BREAKPOINT[2] = { 0x00, 0xBE };
constexpr unsigned char ARM_BREAKPOINT[4] = { 0x70, 0x00, 0x20, 0xE1 };
bool is_thumb16(uint32_t inst) {
inline bool is_thumb16(uint32_t inst) {
return (inst & 0xF8000000) < 0xE8000000;
}
@ -27,6 +28,7 @@ void Debugger::add_breakpoint(MemState &mem, uint32_t addr, bool thumb_mode) {
std::memcpy(&mem.memory[addr], ARM_BREAKPOINT, sizeof(ARM_BREAKPOINT));
}
breakpoints.emplace(addr, last);
parent->invalidate_jit_cache(addr, 4);
}
void Debugger::remove_breakpoint(MemState &mem, uint32_t addr) {
@ -34,41 +36,56 @@ void Debugger::remove_breakpoint(MemState &mem, uint32_t addr) {
auto last = breakpoints[addr];
std::memcpy(&mem.memory[addr], &last.data, last.thumb_mode ? sizeof(THUMB_BREAKPOINT) : sizeof(ARM_BREAKPOINT));
breakpoints.erase(addr);
parent->invalidate_jit_cache(addr, 4);
}
}
void Debugger::add_trampoile(MemState &mem, uint32_t addr, bool thumb_mode, TrampolineCallback callback) {
const auto encode_thumb_and_swap = [](uint8_t type, uint32_t immed, uint16_t reg) {
const uint32_t inst = encode_thumb_inst(type, immed, reg);
return (inst << 16) | ((inst >> 16) & 0xFFFF);
};
const auto encode_inst = thumb_mode ? encode_thumb_and_swap : encode_arm_inst;
std::unique_ptr<Trampoline> tr = std::make_unique<Trampoline>();
tr->addr = addr;
tr->thumb_mode = thumb_mode;
tr->callback = callback;
uint32_t *insts = reinterpret_cast<uint32_t *>(&mem.memory[addr]);
memcpy(tr->original, insts, 12);
tr->trampoline_code = alloc_block(mem, 0x1C, "trampoline");
tr->trampoline_code = alloc_block(mem, 0x20, "trampoline");
tr->lr = tr->addr + 12;
if (thumb_mode)
tr->lr |= 1;
const Address trampoline_addr = align(tr->trampoline_code.get(), 4);
const auto encode_inst = thumb_mode ? encode_thumb_inst : encode_arm_inst;
const Address imm = thumb_mode ? trampoline_addr | 1 : trampoline_addr;
insts[0] = encode_inst(INSTRUCTION_MOVW, (uint16_t)imm, 12);
insts[1] = encode_inst(INSTRUCTION_MOVT, (uint16_t)(imm >> 16), 12);
const Address trampoline_addr = align(tr->trampoline_code.get(), 4);
// Insert trampoline jumper and back up
uint32_t *insts = reinterpret_cast<uint32_t *>(&mem.memory[addr]);
memcpy(tr->original, insts, 12);
const Address destination = thumb_mode ? trampoline_addr | 1 : trampoline_addr;
insts[0] = encode_inst(INSTRUCTION_MOVW, (uint16_t)destination, 12);
insts[1] = encode_inst(INSTRUCTION_MOVT, (uint16_t)(destination >> 16), 12);
insts[2] = encode_inst(INSTRUCTION_BRANCH, 0, 12);
// Create trampoline
uint32_t *trampoline_insts = reinterpret_cast<uint32_t *>(&mem.memory[trampoline_addr]);
memcpy(trampoline_insts, tr->original, 12);
trampoline_insts[3] = thumb_mode ? 0xDF53BF00 : 0xEF000053; // SVC 0x53
uint64_t *trampoline_ptr = reinterpret_cast<uint64_t *>(&trampoline_insts[4]);
*trampoline_ptr = reinterpret_cast<uintptr_t>(tr.get());
Trampoline **trampoline_host_ptr = Ptr<Trampoline *>(trampoline_addr + 16).get(mem);
*trampoline_host_ptr = tr.get();
std::lock_guard<std::mutex> lock(mutex);
trampolines.emplace(addr, std::move(tr));
parent->invalidate_jit_cache(addr, 12);
}
void Debugger::remove_trampoline(MemState &mem, uint32_t addr) {
auto it = trampolines.find(addr);
std::lock_guard<std::mutex> lock(mutex);
const auto it = trampolines.find(addr);
if (it != trampolines.end()) {
uint32_t *insts = reinterpret_cast<uint32_t *>(&mem.memory[addr]);
memcpy(insts, it->second->original, 12);
trampolines.erase(addr);
trampolines.erase(it);
parent->invalidate_jit_cache(addr, 12);
}
}
@ -94,19 +111,5 @@ Address Debugger::get_watch_memory_addr(Address addr) {
}
void Debugger::update_watches() {
for (const auto &thread : parent->threads) {
auto &cpu = *thread.second->cpu;
if (watch_code != get_log_code(cpu)) {
if (watch_code)
set_log_code(cpu, true);
else
set_log_code(cpu, false);
}
if (watch_memory != get_log_mem(cpu)) {
if (watch_memory)
set_log_mem(cpu, true);
else
set_log_mem(cpu, false);
}
}
parent->set_memory_watch(watch_memory);
}

View File

@ -24,7 +24,8 @@
#define INSTRUCTION_MOVT 2 ///< MOVT Rd, \#imm instruction
#define INSTRUCTION_SYSCALL 3 ///< SVC \#imm instruction
#define INSTRUCTION_BRANCH 4 ///< BX Rn instruction
#define INSTRUCTION_BLX 5 ///< BLX imm instruction
uint32_t encode_arm_inst(uint8_t type, uint16_t immed, uint16_t reg);
uint32_t encode_arm_inst(uint8_t type, uint32_t immed, uint16_t reg);
// SVC not implemented for thumb
uint32_t encode_thumb_inst(uint8_t type, uint16_t immed, uint16_t reg);
uint32_t encode_thumb_inst(uint8_t type, uint32_t immed, uint16_t reg);

View File

@ -332,7 +332,7 @@ std::uint32_t nearest_power_of_two(std::uint32_t num) {
// Encode code taken from https://github.com/yifanlu/UVLoader/blob/master/resolve.c
uint32_t encode_arm_inst(uint8_t type, uint16_t immed, uint16_t reg) {
uint32_t encode_arm_inst(uint8_t type, uint32_t immed, uint16_t reg) {
switch (type) {
case INSTRUCTION_MOVW:
// 1110 0011 0000 XXXX YYYY XXXXXXXXXXXX
@ -352,20 +352,35 @@ uint32_t encode_arm_inst(uint8_t type, uint16_t immed, uint16_t reg) {
// 1110 0001 0010 111111111111 0001 YYYY
// BX Rn has 0xE12FFF1 as top bytes
return ((uint32_t)0xE12FFF1 << 4) | reg;
case INSTRUCTION_BLX:
return ((uint32_t)0x7D << 25) | ((immed & 0x80000000) >> 8) | (immed & 0x7fffff);
case INSTRUCTION_UNKNOWN:
default:
return 0;
}
}
uint32_t encode_thumb_inst(uint8_t type, uint16_t immed, uint16_t reg) {
inline uint32_t encode_thumb_blx(uint32_t immed) {
const uint32_t S = (immed & 0x80000000) >> 31;
const uint32_t I1 = (immed & 0x800000) >> 22;
const uint32_t I2 = (immed & 0x400000) >> 21;
const uint32_t immhi = (immed & 0x3ff000) >> 11;
const uint32_t immlo = (immed & 0xffc) >> 2;
const uint32_t J1 = ~I1 ^ S;
const uint32_t J2 = ~I2 ^ S;
return ((uint32_t)0x1E << 27) | (S << 26) | (immhi << 16) | ((uint32_t)0x3 << 14) | (J1 << 13) | (J2 << 11) | (immlo << 1);
}
uint32_t encode_thumb_inst(uint8_t type, uint32_t immed, uint16_t reg) {
switch (type) {
case INSTRUCTION_MOVW:
return ((uint32_t)0x1E << 27) | ((uint32_t)(immed & 0x1000) << 14) | ((uint32_t)0x24 << 20) | ((immed & 0xf000) << 4) | ((immed & 0x700) << 4) | (reg << 8) | immed & 0xff;
return ((uint32_t)0x1E << 27) | ((uint32_t)(immed & 0x800) << 15) | ((uint32_t)0x24 << 20) | ((immed & 0xf000) << 4) | ((immed & 0x700) << 4) | (reg << 8) | (immed & 0xff);
case INSTRUCTION_MOVT:
return ((uint32_t)0x1E << 27) | ((uint32_t)(immed & 0x1000) << 14) | ((uint32_t)0x2C << 20) | ((immed & 0xf000) << 4) | ((immed & 0x700) << 4) | (reg << 8) | immed & 0xff;
return ((uint32_t)0x1E << 27) | ((uint32_t)(immed & 0x800) << 15) | ((uint32_t)0x2C << 20) | ((immed & 0xf000) << 4) | ((immed & 0x700) << 4) | (reg << 8) | (immed & 0xff);
case INSTRUCTION_BRANCH:
return ((((uint32_t)0x8E << 7) | (reg << 3)) << 16) | 0xBF00;
return ((((uint32_t)0x8E << 7) | (reg << 3)) << 16) | (uint32_t)0xBF00;
case INSTRUCTION_BLX:
return encode_thumb_blx(immed);
case INSTRUCTION_UNKNOWN:
default:
return 0;