Fix: Recover from null pointer execution crashes

Add proper recovery mechanism for null pointer execution (PC < 0x1000)
by returning from invalid function calls using the Link Register instead
of blindly continuing execution.

Fixes infinite crash loop in games like Little Nightmares III.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-10-11 17:02:28 +10:00
parent 3c16d8330b
commit fbb4f5c015
2 changed files with 43 additions and 4 deletions

View File

@@ -13,6 +13,17 @@ void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
Kernel::Svc::ThreadContext ctx; Kernel::Svc::ThreadContext ctx;
this->GetContext(ctx); this->GetContext(ctx);
// Check if this is a null pointer execution (PC in very low memory)
bool is_null_pointer_execution = ctx.pc < 0x1000;
// Only show detailed backtrace for the first occurrence or non-null-pointer crashes
if (is_null_pointer_execution) {
LOG_WARNING(Core_ARM, "Null pointer execution at pc={:016X}, sp={:016X}, lr={:016X}",
ctx.pc, ctx.sp, ctx.lr);
LOG_WARNING(Core_ARM, "Will attempt recovery by returning from function");
return;
}
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", ctx.sp, ctx.pc); LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", ctx.sp, ctx.pc);
LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
"Offset", "Symbol"); "Offset", "Symbol");

View File

@@ -125,11 +125,39 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
if (prefetch_abort) { if (prefetch_abort) {
LOG_WARNING(Core_ARM, "Prefetch abort detected - checking if recoverable..."); LOG_WARNING(Core_ARM, "Prefetch abort detected - checking if recoverable...");
// For Nintendo SDK crashes, try to continue execution // Get the current PC to check if we're in a null pointer execution loop
Kernel::Svc::ThreadContext ctx;
interface->GetContext(ctx);
u64 current_pc = ctx.pc;
// Detect null pointer execution loop (PC in very low memory addresses)
if (current_pc < 0x1000) {
LOG_WARNING(Core_ARM, "Null pointer execution detected at PC={:016X}", current_pc);
LOG_WARNING(Core_ARM, "Attempting to recover by returning from invalid function call");
// Try to recover by returning from the function using LR (X30)
// This simulates a function return
u64 return_address = ctx.lr;
if (return_address != 0 && return_address >= 0x1000) {
LOG_INFO(Core_ARM, "Recovering: Setting PC to return address {:016X}", return_address);
ctx.pc = return_address;
// Set return value to 0 in X0
ctx.r[0] = 0;
interface->SetContext(ctx);
should_continue = true;
} else {
LOG_ERROR(Core_ARM, "Cannot recover: Invalid return address {:016X}", return_address);
LOG_ERROR(Core_ARM, "Thread will be suspended due to unrecoverable crash");
should_continue = false;
}
} else {
// For Nintendo SDK crashes at valid addresses, try to continue execution
// This is especially important for games like Phoenix Switch // This is especially important for games like Phoenix Switch
should_continue = true; should_continue = true;
LOG_INFO(Core_ARM, "Attempting to continue execution after Nintendo SDK crash"); LOG_INFO(Core_ARM, "Attempting to continue execution after Nintendo SDK crash");
} }
}
} else { } else {
system.GetDebugger().NotifyThreadStopped(thread); system.GetDebugger().NotifyThreadStopped(thread);
} }