mirror of
https://github.com/Vita3K/Vita3K-Android.git
synced 2024-12-04 11:43:39 +00:00
kernel: Polish trampoline injection
This commit is contained in:
parent
7d6d79c399
commit
be53a21f2f
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user