From 139bc7b9816939bf9a00905cb12656d387cdcd2d Mon Sep 17 00:00:00 2001 From: ivica00 Date: Mon, 12 Oct 2026 23:34:22 +0200 Subject: [PATCH] feat(libc): optionally dump indirect memory in Windows crash reports --- .../prx/libc/src/specifics/windows/CrashReport.cpp | 13 +++++++++++++ docs/user/USAGE.md | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/core/libs/prx/libc/src/specifics/windows/CrashReport.cpp b/core/libs/prx/libc/src/specifics/windows/CrashReport.cpp index a4671a43eb..f393d809b4 100644 --- a/core/libs/prx/libc/src/specifics/windows/CrashReport.cpp +++ b/core/libs/prx/libc/src/specifics/windows/CrashReport.cpp @@ -282,11 +282,24 @@ LONG WINAPI ReportCrash(EXCEPTION_POINTERS* info) { Report(" r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n", context->R12, context->R13, context->R14, context->R15); // The words each register points at, to recognise which structure a bad pointer came from. { + char indirectBuffer[16]{}; + const bool indirectMemory = GetEnvironmentVariableA("APS5_CRASH_INDIRECT_MEMORY", indirectBuffer, sizeof(indirectBuffer)) > 0 && indirectBuffer[0] != '\0'; const std::pair pointers[] = {{"rax", context->Rax}, {"rbx", context->Rbx}, {"rcx", context->Rcx}, {"rdx", context->Rdx}, {"rsi", context->Rsi}, {"rdi", context->Rdi}, {"r12", context->R12}, {"r13", context->R13}, {"r14", context->R14}, {"r15", context->R15}}; for (const auto& [name, value] : pointers) { if (value < 0x10000 || (value & 7) != 0 || !IsReadable(value) || !IsReadable(value + 0x38)) continue; const auto* words = reinterpret_cast(value); Report(" [%s] %016llx %016llx %016llx %016llx %016llx %016llx %016llx %016llx\n", name, words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7]); + if (!indirectMemory) continue; + for (std::size_t i = 0; i < 8; ++i) { + const auto address = words[i]; + if (address < 0x10000 || IsExecutable(address)) continue; + std::uint64_t content[8]{}; + SIZE_T copied = 0; + if (!ReadProcessMemory(GetCurrentProcess(), reinterpret_cast(address), content, sizeof(content), &copied) || copied != sizeof(content)) continue; + Report(" [[%s+0x%llx]] @%016llx %016llx %016llx %016llx %016llx %016llx %016llx %016llx %016llx\n", + name, static_cast(i * sizeof(*words)), static_cast(address), + content[0], content[1], content[2], content[3], content[4], content[5], content[6], content[7]); + } } } Report(" frame pointer chain:\n"); diff --git a/docs/user/USAGE.md b/docs/user/USAGE.md index 4e75ad5559..94682e762e 100644 --- a/docs/user/USAGE.md +++ b/docs/user/USAGE.md @@ -171,6 +171,10 @@ most called traced imports (1 calls in total): Limits are listed in the [libc](../dev/debt/libc.md) and [relinker](../dev/debt/relinker.md) technical debt. +### Windows crash diagnostics + +Set `APS5_CRASH_INDIRECT_MEMORY=1` before launching the executable to include up to 64 bytes from each pointer found in the register memory dump. The report labels each block with its register, pointer offset and target address. Inaccessible, incomplete and executable targets are skipped. This additional dump is disabled when the variable is unset or empty. + ## Exit codes `0`: conversion succeeded or help was displayed. `1`: invalid arguments. `2`: conversion failed; the error is printed to stderr. With `--autorun`, successful conversion returns the launched application's exit code.