Decompile crashGetParentStackFrame

This commit is contained in:
Ryan Dwyer 2021-03-15 17:02:03 +10:00
parent 37de58e244
commit e034b62c07
2 changed files with 203 additions and 174 deletions

View File

@ -88,7 +88,7 @@ extern const char var700529a8[];
extern const char var700529ac[]; extern const char var700529ac[];
void faultCreateThread2(void); void faultCreateThread2(void);
s32 func0000c118(u32 ptr, void *arg1, u32 sp, u32 *arg3); u32 crashGetParentStackFrame(u32 *ptr, u32 *start, u32 sp, u32 *regs);
bool crashIsReturnAddress(u32 *instruction); bool crashIsReturnAddress(u32 *instruction);
u32 crashGetStackEnd(u32 sp, s32 tid); u32 crashGetStackEnd(u32 sp, s32 tid);
u32 crashGetStackStart(u32 arg0, s32 tid); u32 crashGetStackStart(u32 arg0, s32 tid);

View File

@ -8,7 +8,7 @@
#include "data.h" #include "data.h"
#include "types.h" #include "types.h"
u32 var8005d5b0 = 0x00000000; u32 var8005d5b0 = 0;
s16 g_CrashCurX = 0; s16 g_CrashCurX = 0;
s16 g_CrashCurY = 0; s16 g_CrashCurY = 0;
@ -19,91 +19,94 @@ struct crashdescription {
}; };
struct crashdescription g_CrashCauseDescriptions[] = { struct crashdescription g_CrashCauseDescriptions[] = {
{ 0x80000000, 0x80000000, "BD" }, { CAUSE_BD, CAUSE_BD, "BD" },
{ 0x00008000, 0x00008000, "IP8" }, { CAUSE_IP8, CAUSE_IP8, "IP8" },
{ 0x00004000, 0x00004000, "IP7" }, { CAUSE_IP7, CAUSE_IP7, "IP7" },
{ 0x00002000, 0x00002000, "IP6" }, { CAUSE_IP6, CAUSE_IP6, "IP6" },
{ 0x00001000, 0x00001000, "IP5" }, { CAUSE_IP5, CAUSE_IP5, "IP5" },
{ 0x00000800, 0x00000800, "IP4" }, { CAUSE_IP4, CAUSE_IP4, "IP4" },
{ 0x00000400, 0x00000400, "IP3" }, { CAUSE_IP3, CAUSE_IP3, "IP3" },
{ 0x00000200, 0x00000200, "IP2" }, { CAUSE_SW2, CAUSE_SW2, "IP2" },
{ 0x00000100, 0x00000100, "IP1" }, { CAUSE_SW1, CAUSE_SW1, "IP1" },
{ 0x0000007c, 0x00000000, "Int" }, { CAUSE_EXCMASK, EXC_INT, "Int" },
{ 0x0000007c, 0x00000004, "TLBmod" }, { CAUSE_EXCMASK, EXC_MOD, "TLBmod" },
{ 0x0000007c, 0x00000008, "TLBload" }, { CAUSE_EXCMASK, EXC_RMISS, "TLBload" },
{ 0x0000007c, 0x0000000c, "TLBstore" }, { CAUSE_EXCMASK, EXC_WMISS, "TLBstore" },
{ 0x0000007c, 0x00000010, "Address error on load or instruction fetch" }, { CAUSE_EXCMASK, EXC_RADE, "Address error on load or instruction fetch" },
{ 0x0000007c, 0x00000014, "Address error on store" }, { CAUSE_EXCMASK, EXC_WADE, "Address error on store" },
{ 0x0000007c, 0x00000018, "Bus error exception on instruction fetch" }, { CAUSE_EXCMASK, EXC_IBE, "Bus error exception on instruction fetch" },
{ 0x0000007c, 0x0000001c, "Bus error exception on data reference" }, { CAUSE_EXCMASK, EXC_DBE, "Bus error exception on data reference" },
{ 0x0000007c, 0x00000020, "Syscall" }, { CAUSE_EXCMASK, EXC_SYSCALL, "Syscall" },
{ 0x0000007c, 0x00000024, "Brk" }, { CAUSE_EXCMASK, EXC_BREAK, "Brk" },
{ 0x0000007c, 0x00000028, "Reserved instruction" }, { CAUSE_EXCMASK, EXC_II, "Reserved instruction" },
{ 0x0000007c, 0x0000002c, "Cop unusable" }, { CAUSE_EXCMASK, EXC_CPU, "Cop unusable" },
{ 0x0000007c, 0x00000030, "Overflow" }, { CAUSE_EXCMASK, EXC_OV, "Overflow" },
{ 0x0000007c, 0x00000034, "Trap" }, { CAUSE_EXCMASK, EXC_TRAP, "Trap" },
{ 0x0000007c, 0x00000038, "Virtual coherency exception on intruction fetch" }, { CAUSE_EXCMASK, EXC_VCEI, "Virtual coherency exception on intruction fetch" },
{ 0x0000007c, 0x0000003c, "Fp exception" }, { CAUSE_EXCMASK, EXC_FPE, "Fp exception" },
{ 0x0000007c, 0x0000005c, "Watchpoint" }, { CAUSE_EXCMASK, EXC_WATCH, "Watchpoint" },
{ 0x0000007c, 0x0000007c, "Virtual coherency exception on data reference" }, { CAUSE_EXCMASK, EXC_VCED, "Virtual coherency exception on data reference" },
{ 0x00000000, 0x00000000, "" }, { 0, 0, "" },
{ 0x80000000, 0x80000000, "CU3" }, };
{ 0x40000000, 0x40000000, "CU2" },
{ 0x20000000, 0x20000000, "CU1" }, struct crashdescription g_CrashSrDescriptions[] = {
{ 0x10000000, 0x10000000, "CU0" }, { SR_CU3, SR_CU3, "CU3" },
{ 0x08000000, 0x08000000, "RP" }, { SR_CU2, SR_CU2, "CU2" },
{ 0x04000000, 0x04000000, "FR" }, { SR_CU1, SR_CU1, "CU1" },
{ 0x02000000, 0x02000000, "RE" }, { SR_CU0, SR_CU0, "CU0" },
{ 0x00400000, 0x00400000, "BEV" }, { SR_RP, SR_RP, "RP" },
{ 0x00200000, 0x00200000, "TS" }, { SR_FR, SR_FR, "FR" },
{ 0x00100000, 0x00100000, "SR" }, { SR_RE, SR_RE, "RE" },
{ 0x00040000, 0x00040000, "CH" }, { SR_BEV, SR_BEV, "BEV" },
{ 0x00020000, 0x00020000, "CE" }, { SR_TS, SR_TS, "TS" },
{ 0x00010000, 0x00010000, "DE" }, { SR_SR, SR_SR, "SR" },
{ 0x00008000, 0x00008000, "IM8" }, { SR_CH, SR_CH, "CH" },
{ 0x00004000, 0x00004000, "IM7" }, { SR_CE, SR_CE, "CE" },
{ 0x00002000, 0x00002000, "IM6" }, { SR_DE, SR_DE, "DE" },
{ 0x00001000, 0x00001000, "IM5" }, { SR_IBIT8, SR_IBIT8, "IM8" },
{ 0x00000800, 0x00000800, "IM4" }, { SR_IBIT7, SR_IBIT7, "IM7" },
{ 0x00000400, 0x00000400, "IM3" }, { SR_IBIT6, SR_IBIT6, "IM6" },
{ 0x00000200, 0x00000200, "IM2" }, { SR_IBIT5, SR_IBIT5, "IM5" },
{ 0x00000100, 0x00000100, "IM1" }, { SR_IBIT4, SR_IBIT4, "IM4" },
{ 0x00000080, 0x00000080, "KX" }, { SR_IBIT3, SR_IBIT3, "IM3" },
{ 0x00000040, 0x00000040, "SX" }, { SR_IBIT2, SR_IBIT2, "IM2" },
{ 0x00000020, 0x00000020, "UX" }, { SR_IBIT1, SR_IBIT1, "IM1" },
{ 0x00000018, 0x00000010, "USR" }, { SR_KX, SR_KX, "KX" },
{ 0x00000018, 0x00000008, "SUP" }, { SR_SX, SR_SX, "SX" },
{ 0x00000018, 0x00000000, "KER" }, { SR_UX, SR_UX, "UX" },
{ 0x00000004, 0x00000004, "ERL" }, { SR_KSU_MASK, SR_KSU_USR, "USR" },
{ 0x00000002, 0x00000002, "EXL" }, { SR_KSU_MASK, SR_KSU_SUP, "SUP" },
{ 0x00000001, 0x00000001, "IE" }, { SR_KSU_MASK, SR_KSU_KER, "KER" },
{ 0x00000000, 0x00000000, "" }, { SR_ERL, SR_ERL, "ERL" },
{ SR_EXL, SR_EXL, "EXL" },
{ SR_IE, SR_IE, "IE" },
{ 0, 0, "" },
}; };
struct crashdescription g_CrashFpcsrDescriptions[] = { struct crashdescription g_CrashFpcsrDescriptions[] = {
{ 0x01000000, 0x01000000, "FS" }, { FPCSR_FS, FPCSR_FS, "FS" },
{ 0x00800000, 0x00800000, "C" }, { FPCSR_C, FPCSR_C, "C" },
{ 0x00020000, 0x00020000, "Unimplemented" }, { FPCSR_CE, FPCSR_CE, "Unimplemented" },
{ 0x00010000, 0x00010000, "Invalid op" }, { FPCSR_CV, FPCSR_CV, "Invalid op" },
{ 0x00008000, 0x00008000, "/ by 0.0" }, { FPCSR_CZ, FPCSR_CZ, "/ by 0.0" },
{ 0x00004000, 0x00004000, "Overflow" }, { FPCSR_CO, FPCSR_CO, "Overflow" },
{ 0x00002000, 0x00002000, "Underflow" }, { FPCSR_CU, FPCSR_CU, "Underflow" },
{ 0x00001000, 0x00001000, "Inexact op" }, { FPCSR_CI, FPCSR_CI, "Inexact op" },
{ 0x00000800, 0x00000800, "EV" }, { FPCSR_EV, FPCSR_EV, "EV" },
{ 0x00000400, 0x00000400, "EZ" }, { FPCSR_EZ, FPCSR_EZ, "EZ" },
{ 0x00000200, 0x00000200, "EO" }, { FPCSR_EO, FPCSR_EO, "EO" },
{ 0x00000100, 0x00000100, "EU" }, { FPCSR_EU, FPCSR_EU, "EU" },
{ 0x00000080, 0x00000080, "EI" }, { FPCSR_EI, FPCSR_EI, "EI" },
{ 0x00000040, 0x00000040, "FV" }, { FPCSR_FV, FPCSR_FV, "FV" },
{ 0x00000020, 0x00000020, "FZ" }, { FPCSR_FZ, FPCSR_FZ, "FZ" },
{ 0x00000010, 0x00000010, "FO" }, { FPCSR_FO, FPCSR_FO, "FO" },
{ 0x00000008, 0x00000008, "FU" }, { FPCSR_FU, FPCSR_FU, "FU" },
{ 0x00000004, 0x00000004, "FI" }, { FPCSR_FI, FPCSR_FI, "FI" },
{ 0x00000003, 0x00000000, "RN" }, { FPCSR_RM_MASK, FPCSR_RM_RN, "RN" },
{ 0x00000003, 0x00000001, "RZ" }, { FPCSR_RM_MASK, FPCSR_RM_RZ, "RZ" },
{ 0x00000003, 0x00000002, "RP" }, { FPCSR_RM_MASK, FPCSR_RM_RP, "RP" },
{ 0x00000003, 0x00000003, "RM" }, { FPCSR_RM_MASK, FPCSR_RM_RM, "RM" },
{ 0x00000000, 0x00000000, "" }, { 0, 0, "" },
}; };
char (*g_CrashCharBuffer)[71] = NULL; char (*g_CrashCharBuffer)[71] = NULL;
@ -198,92 +201,118 @@ glabel faultproc
// } // }
//} //}
GLOBAL_ASM( /**
glabel func0000c118 * Given a pointer to an instruction and a stack frame pointer, attempt to find
/* c118: 27bdfff0 */ addiu $sp,$sp,-16 * the calling function. Return the address of the caller's stack frame and
/* c11c: afa40010 */ sw $a0,0x10($sp) * populate regs with stack addresses where that register was saved. This can be
/* c120: afb1000c */ sw $s1,0xc($sp) * used to retrieve the RA value and invoke crashGetParentStackFrame again to
/* c124: afb00008 */ sw $s0,0x8($sp) * build a backtrace.
/* c128: 00c01825 */ or $v1,$a2,$zero *
/* c12c: 00001025 */ or $v0,$zero,$zero * origptr is a pointer to an instruction. This should be either the value of
/* c130: 00004025 */ or $t0,$zero,$zero * the PC register of the faulted thread, or an RA register if searching a
/* c134: 24040020 */ addiu $a0,$zero,0x20 * parent.
/* c138: 00004825 */ or $t1,$zero,$zero * minaddr is the memory address of the start of the code segment,
/* c13c: 00e05025 */ or $t2,$a3,$zero * ie. 0x70001050. This is used to prevent the function from walking out of
.L0000c140: * the code segment.
/* c140: 25290001 */ addiu $t1,$t1,0x1 * origsp is the address of the stack frame for the given origptr.
/* c144: ad400000 */ sw $zero,0x0($t2) * regs is a pointer to an array of 32 words.
/* c148: 1524fffd */ bne $t1,$a0,.L0000c140 *
/* c14c: 254a0004 */ addiu $t2,$t2,0x4 * The function works by walking backwards one instruction at a time, looking
/* c150: 8fa40010 */ lw $a0,0x10($sp) * for stack frame adjustments and stores of $ra to the stack. Once both of
/* c154: 3c1103e0 */ lui $s1,0x3e0 * these are are found, the function returns with this information.
/* c158: 36310008 */ ori $s1,$s1,0x8 *
/* c15c: 0085082b */ sltu $at,$a0,$a1 * The function will return 0 if it can't reliably find the caller. This will
/* c160: 1420002d */ bnez $at,.L0000c218 * happen if the function being analysed didn't adjust the stack pointer or
/* c164: 2410001f */ addiu $s0,$zero,0x1f * store $ra in the stack. It can also fail if the function being analysed uses
/* c168: 3c0dffe0 */ lui $t5,0xffe0 * returns within branches.
/* c16c: 3c0cafa0 */ lui $t4,0xafa0 */
/* c170: 3c0bffff */ lui $t3,0xffff u32 crashGetParentStackFrame(u32 *origptr, u32 *minaddr, u32 origsp, u32 *regs)
/* c174: 3c0a27bd */ lui $t2,0x27bd {
/* c178: 8c890000 */ lw $t1,0x0($a0) u32 sp = origsp;
.L0000c17c: u32 *ptr;
/* c17c: 2484fffc */ addiu $a0,$a0,-4 bool foundaddsp = false;
/* c180: 0085082b */ sltu $at,$a0,$a1 bool foundra = false;
/* c184: 012b7824 */ and $t7,$t1,$t3 s32 regid;
/* c188: 154f000b */ bne $t2,$t7,.L0000c1b8 u32 instruction;
/* c18c: 012dc024 */ and $t8,$t1,$t5 s16 value;
/* c190: 0009c400 */ sll $t8,$t1,0x10
/* c194: 0018cc03 */ sra $t9,$t8,0x10 // Clear the regs array (note: reusing the instruction variable here)
/* c198: 1f20001f */ bgtz $t9,.L0000c218 for (instruction = 0; instruction < 32; instruction++) regs[instruction] = 0;
/* c19c: 24020001 */ addiu $v0,$zero,0x1
/* c1a0: 00197083 */ sra $t6,$t9,0x2 // Walk backwards through the instructions
/* c1a4: 000e7880 */ sll $t7,$t6,0x2 for (ptr = origptr; ptr >= minaddr; ptr--) {
/* c1a8: 11000019 */ beqz $t0,.L0000c210 instruction = *ptr;
/* c1ac: 006f1823 */ subu $v1,$v1,$t7 value = instruction & 0xffff;
/* c1b0: 10000019 */ b .L0000c218
/* c1b4: 00000000 */ nop if ((instruction & 0xffff0000) == 0x27bd0000) {
.L0000c1b8: // Found an addiu $sp, $sp, <amount> instruction, which adjusts the
/* c1b8: 15980013 */ bne $t4,$t8,.L0000c208 // stack pointer. These can exist near the start and end of any
/* c1bc: 00093402 */ srl $a2,$t1,0x10 // function. The "add" at the start is done with a negative value
/* c1c0: 30d9001f */ andi $t9,$a2,0x1f // which increases the size of the stack, as the stack expands to
/* c1c4: 00097c00 */ sll $t7,$t1,0x10 // the left. This function is interested in these negative adds,
/* c1c8: 000fc403 */ sra $t8,$t7,0x10 // because it needs to reverse it and move the sp variable forward
/* c1cc: 03203025 */ or $a2,$t9,$zero // to the next stack frame (the frame of the caller).
/* c1d0: 0018c883 */ sra $t9,$t8,0x2
/* c1d4: 00197080 */ sll $t6,$t9,0x2 foundaddsp = true;
/* c1d8: 0006c080 */ sll $t8,$a2,0x2
/* c1dc: 00f8c821 */ addu $t9,$a3,$t8 if (value > 0) {
/* c1e0: 01c37821 */ addu $t7,$t6,$v1 // Found the addiu sp at the end of the function. It's pretty
/* c1e4: 16060002 */ bne $s0,$a2,.L0000c1f0 // rare to crash (or jump elsewhere) after restoring the sp,
/* c1e8: af2f0000 */ sw $t7,0x0($t9) // so this situation is not supported by this function.
/* c1ec: 24080001 */ addiu $t0,$zero,0x1 break;
.L0000c1f0: }
/* c1f0: 10400007 */ beqz $v0,.L0000c210
/* c1f4: 00000000 */ nop // Change sp to point to the caller's stack frame
/* c1f8: 11000005 */ beqz $t0,.L0000c210 sp -= (value >> 2) * 4;
/* c1fc: 00000000 */ nop
/* c200: 10000005 */ b .L0000c218 if (foundra) {
/* c204: 00000000 */ nop break;
.L0000c208: }
/* c208: 11310003 */ beq $t1,$s1,.L0000c218 } else if ((instruction & 0xffe00000) == 0xafa00000) {
/* c20c: 00000000 */ nop // Found a store word instruction that stores to the stack.
.L0000c210: // The encoding of this instruction is:
/* c210: 5020ffda */ beqzl $at,.L0000c17c //
/* c214: 8c890000 */ lw $t1,0x0($a0) // oooooodd dddsssss iiiiiiii iiiiiiii
.L0000c218: //
/* c218: 10400005 */ beqz $v0,.L0000c230 // Where:
/* c21c: 8fb00008 */ lw $s0,0x8($sp) // o is the opcode (already known to be sw).
/* c220: 51000004 */ beqzl $t0,.L0000c234 // d is the destination register (already known to be $sp)
/* c224: 00001025 */ or $v0,$zero,$zero // s is the source register
/* c228: 10000002 */ b .L0000c234 // i is the offset to store to
/* c22c: 00601025 */ or $v0,$v1,$zero //
.L0000c230: // This looks for a store from $ra to the stack, so the value can
/* c230: 00001025 */ or $v0,$zero,$zero // be read from the stack and used to find the caller's address.
.L0000c234:
/* c234: 8fb1000c */ lw $s1,0xc($sp) regid = (instruction >> 16) & 0x1f;
/* c238: 03e00008 */ jr $ra regs[regid] = sp + (value >> 2) * 4;
/* c23c: 27bd0010 */ addiu $sp,$sp,0x10
); if (regid == 31) {
foundra = true;
}
if (foundaddsp && foundra) {
break;
}
} else if (instruction == 0x03e00008) {
// Found a jr $ra statement, which is a return. This will happen if
// this loop has walked out of the current function and into the one
// prior to it, so bail.
// It can also happen if the function has return statements within
// branches, but handling these correctly would involve a lot of
// complexity so that's unsupported. Because of this, this function
// can end the backtrace prematurely if it encounters a function
// that does this.
break;
}
}
if (foundaddsp && foundra) {
return sp;
}
return 0;
}
bool crashIsReturnAddress(u32 *instruction) bool crashIsReturnAddress(u32 *instruction)
{ {
@ -409,7 +438,7 @@ u32 crashRender(OSThread *thread, u32 *callstack, s32 *tracelen)
s32 i; s32 i;
u32 ptr; u32 ptr;
u32 *sp; u32 *sp;
u32 sp64[32]; u32 regs[32];
u32 *stackend; u32 *stackend;
u32 *stackstart; u32 *stackstart;
__OSThreadContext *ctx = &thread->context; __OSThreadContext *ctx = &thread->context;
@ -493,7 +522,7 @@ u32 crashRender(OSThread *thread, u32 *callstack, s32 *tracelen)
crashPrint("nearl: "); crashPrint("nearl: ");
while (!done) { while (!done) {
sp = (u32 *) func0000c118(ptr, &_libSegmentStart, (u32)sp, sp64); sp = (u32 *) crashGetParentStackFrame((u32 *) ptr, &_libSegmentStart, (u32)sp, regs);
crashPrint(" %08x ", ptr); crashPrint(" %08x ", ptr);
callstack[*tracelen] = ptr; callstack[*tracelen] = ptr;
@ -507,10 +536,10 @@ u32 crashRender(OSThread *thread, u32 *callstack, s32 *tracelen)
sp = (u32 *)ctx->sp; sp = (u32 *)ctx->sp;
} }
if (sp64[31] == 0) { if (regs[31] == 0) {
ptr = ctx->ra; ptr = ctx->ra;
} else { } else {
ptr = *(u32 *)(sp64[31]); ptr = *(u32 *)(regs[31]);
} }
if (sp < stackstart || sp >= stackend || stackend == &sp[4] || ptr == 0) { if (sp < stackstart || sp >= stackend || stackend == &sp[4] || ptr == 0) {