1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 12:12:50 +01:00

Exception handling fix

This commit is contained in:
Nekotekina 2015-01-18 16:57:39 +03:00
parent 6627cc9666
commit 6545df2b15
2 changed files with 100 additions and 133 deletions

View File

@ -192,81 +192,112 @@ void decode_x64_reg_op(const u8* code, x64_op_t& decoded_op, x64_reg_t& decoded_
#ifdef _WIN32 #ifdef _WIN32
typedef CONTEXT x64_context;
#define RIP 16
#define X64REG(context, reg) ((&context->Rax)[reg])
#else
typedef ucontext_t x64_context;
typedef decltype(REG_RIP) reg_table_t;
#define RIP 16
static const reg_table_t reg_table[17] =
{
REG_RAX, REG_RCX, REG_RDX, REG_RBX, REG_RSP, REG_RBP, REG_RSI, REG_RDI,
REG_R8, REG_R9, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_RIP
};
#define X64REG(context, reg) (context->uc_mcontext.gregs[reg_table[reg]])
#endif
bool handle_access_violation(const u32 addr, x64_context* context)
{
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) // RawSPU MMIO registers
{
// one x64 instruction is manually decoded and interpreted
x64_op_t op;
x64_reg_t reg;
size_t size;
decode_x64_reg_op((const u8*)X64REG(context, RIP), op, reg, size);
// get x64 reg value (for store operations)
u64 reg_value;
if (reg - X64R32 < 16)
{
// load the value from x64 register
reg_value = (u32)X64REG(context, reg - X64R32);
}
else if (reg == X64_IMM32)
{
// load the immediate value (assuming it's at the end of the instruction)
reg_value = *(u32*)(X64REG(context, RIP) + size - 4);
}
else
{
assert(!"Invalid x64_reg_t value");
}
bool save_reg = false;
switch (op)
{
case X64OP_LOAD:
{
reg_value = re32(Memory.ReadMMIO32(addr));
save_reg = true;
break;
}
case X64OP_STORE:
{
Memory.WriteMMIO32(addr, re32((u32)reg_value));
break;
}
default: assert(!"Invalid x64_op_t value");
}
// save x64 reg value (for load operations)
if (save_reg)
{
if (reg - X64R32 < 16)
{
// store the value into x64 register
X64REG(context, reg - X64R32) = (u32)reg_value;
}
else
{
assert(!"Invalid x64_reg_t value (saving)");
}
}
// skip decoded instruction
X64REG(context, RIP) += size;
return true;
}
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
return false;
}
#ifdef _WIN32
void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp) void _se_translator(unsigned int u, EXCEPTION_POINTERS* pExp)
{ {
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)Memory.GetBaseAddr(); const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)Memory.GetBaseAddr();
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0; const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (u == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull) if (u == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64)
{ {
const u32 addr = (u32)addr64; if (handle_access_violation((u32)addr64, pExp->ContextRecord))
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) // RawSPU MMIO registers
{ {
// one x64 instruction is manually decoded and interpreted
x64_op_t op;
x64_reg_t reg;
size_t size;
decode_x64_reg_op((const u8*)pExp->ContextRecord->Rip, op, reg, size);
// get x64 reg value (for store operations)
u64 reg_value;
if (reg - X64R32 < 16)
{
// load the value from x64 register
reg_value = (u32)(&pExp->ContextRecord->Rax)[reg - X64R32];
}
else if (reg == X64_IMM32)
{
// load the immediate value (assuming it's at the end of the instruction)
reg_value = *(u32*)(pExp->ContextRecord->Rip + size - 4);
}
else
{
assert(!"Invalid x64_reg_t value");
}
bool save_reg = false;
switch (op)
{
case X64OP_LOAD:
{
assert(!is_writing);
reg_value = re32(Memory.ReadMMIO32(addr));
save_reg = true;
break;
}
case X64OP_STORE:
{
assert(is_writing);
Memory.WriteMMIO32(addr, re32((u32)reg_value));
break;
}
default: assert(!"Invalid x64_op_t value");
}
// save x64 reg value (for load operations)
if (save_reg)
{
if (reg - X64R32 < 16)
{
// store the value into x64 register
(&pExp->ContextRecord->Rax)[reg - X64R32] = (u32)reg_value;
}
else
{
assert(!"Invalid x64_reg_t value (saving)");
}
}
// skip decoded instruction
pExp->ContextRecord->Rip += size;
// restore context (further code shouldn't be reached) // restore context (further code shouldn't be reached)
RtlRestoreContext(pExp->ContextRecord, nullptr); RtlRestoreContext(pExp->ContextRecord, nullptr);
// it's dangerous because destructors won't be executed // it's dangerous because destructors won't be executed
} }
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
throw fmt::Format("Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr); throw fmt::Format("Access violation %s location 0x%llx", is_writing ? "writing" : "reading", addr64);
} }
// else some fatal error (should crash) // else some fatal error (should crash)
@ -283,79 +314,15 @@ static const reg_table_t reg_table[16] =
void signal_handler(int sig, siginfo_t* info, void* uct) void signal_handler(int sig, siginfo_t* info, void* uct)
{ {
ucontext_t* const ctx = (ucontext_t*)uct;
const u64 addr64 = (u64)info->si_addr - (u64)Memory.GetBaseAddr(); const u64 addr64 = (u64)info->si_addr - (u64)Memory.GetBaseAddr();
//const bool is_writing = false; // TODO: get it correctly if ((u32)addr64 == addr64 && GetCurrentNamedThread())
if (addr64 < 0x100000000ull && GetCurrentNamedThread())
{ {
const u32 addr = (u32)addr64; if (handle_access_violation((u32)addr64, (ucontext_t*)uct))
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) // RawSPU MMIO registers
{ {
// one x64 instruction is manually decoded and interpreted return; // proceed execution
x64_op_t op;
x64_reg_t reg;
size_t size;
decode_x64_reg_op((const u8*)ctx->uc_mcontext.gregs[REG_RIP], op, reg, size);
// get x64 reg value (for store operations)
u64 reg_value;
if (reg - X64R32 < 16)
{
// load the value from x64 register
reg_value = (u32)ctx->uc_mcontext.gregs[reg_table[reg - X64R32]];
}
else if (reg == X64_IMM32)
{
// load the immediate value (assuming it's at the end of the instruction)
reg_value = *(u32*)(ctx->uc_mcontext.gregs[REG_RIP] + size - 4);
}
else
{
assert(!"Invalid x64_reg_t value");
}
bool save_reg = false;
switch (op)
{
case X64OP_LOAD:
{
//assert(!is_writing);
reg_value = re32(Memory.ReadMMIO32(addr));
save_reg = true;
break;
}
case X64OP_STORE:
{
//assert(is_writing);
Memory.WriteMMIO32(addr, re32((u32)reg_value));
break;
}
default: assert(!"Invalid x64_op_t value");
}
// save x64 reg value (for load operations)
if (save_reg)
{
if (reg - X64R32 < 16)
{
// store the value into x64 register
ctx->uc_mcontext.gregs[reg_table[reg - X64R32]] = (u32)reg_value;
}
else
{
assert(!"Invalid x64_reg_t value (saving)");
}
}
// skip decoded instruction
ctx->uc_mcontext.gregs[REG_RIP] += size;
return; // now execution should proceed
//setcontext(ctx);
} }
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory // TODO: this may be wrong
throw fmt::Format("Access violation %s location 0x%x", /*is_writing ? "writing" : "reading"*/ "at", addr); throw fmt::Format("Access violation %s location 0x%x", /*is_writing ? "writing" : "reading"*/ "at", addr);
} }

View File

@ -1067,5 +1067,5 @@ void cellAudio_init(Module *pxThis)
void cellAudio_load() void cellAudio_load()
{ {
// never called :( // CELL_SYSMODULE AUDIO module is rarely loaded manually, so cellAudio_load() won't be called in every case
} }