Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/shared/minidump.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "shared.h" | |
| 2 #include <imagehlp.h> | |
| 3 | |
| 4 #ifdef _M_ARM64EC | |
| 5 typedef ARM64EC_NT_CONTEXT myCONTEXT; | |
| 6 #else | |
| 7 typedef CONTEXT myCONTEXT; | |
| 8 #endif | |
| 9 struct DumpState { | |
| 10 int state; | |
| 11 myCONTEXT*context; | |
| 12 }; | |
| 13 | |
| 14 __declspec(noinline) static bool safeRead(volatile const void* addr, size_t& dest) | |
| 15 { | |
| 16 __try { | |
| 17 dest = *(const volatile size_t*)addr; | |
| 18 return true; | |
| 19 } | |
| 20 __except (1) { | |
| 21 return false; | |
| 22 } | |
| 23 } | |
| 24 | |
| 25 __declspec(noinline) static bool safeTestReadAccess(volatile const void* addr) | |
| 26 { | |
| 27 size_t dummy; | |
| 28 return safeRead(addr, dummy); | |
| 29 } | |
| 30 | |
| 31 #if defined(_M_ARM64) || defined(_M_ARM64EC) | |
| 32 | |
| 33 BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, | |
| 34 const PMINIDUMP_CALLBACK_INPUT CallbackInput, | |
| 35 PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) | |
| 36 { | |
| 37 static const unsigned STACK_SEARCH_SIZE = 0x400; | |
| 38 static const unsigned MEM_BLOCK_SIZE = 0x400; | |
| 39 | |
| 40 if (CallbackInput->CallbackType == MemoryCallback) { | |
| 41 // Called to get user defined blocks of memory to write until | |
| 42 // callback returns FALSE or CallbackOutput->MemorySize == 0. | |
| 43 | |
| 44 DumpState* ds = (DumpState*)CallbackParam; | |
| 45 switch (ds->state) { | |
| 46 // Save memory referenced by registers. | |
| 47 case 0: CallbackOutput->MemoryBase = ds->context->X0; ds->state++; break; | |
| 48 case 1: CallbackOutput->MemoryBase = ds->context->X1; ds->state++; break; | |
| 49 case 2: CallbackOutput->MemoryBase = ds->context->X2; ds->state++; break; | |
| 50 case 3: CallbackOutput->MemoryBase = ds->context->X3; ds->state++; break; | |
| 51 case 4: CallbackOutput->MemoryBase = ds->context->X4; ds->state++; break; | |
| 52 case 5: CallbackOutput->MemoryBase = ds->context->X5; ds->state++; break; | |
| 53 case 6: CallbackOutput->MemoryBase = ds->context->X6; ds->state++; break; | |
| 54 case 7: CallbackOutput->MemoryBase = ds->context->X7; ds->state++; break; | |
| 55 case 8: CallbackOutput->MemoryBase = ds->context->X8; ds->state++; break; | |
| 56 case 9: CallbackOutput->MemoryBase = ds->context->X9; ds->state++; break; | |
| 57 case 10: CallbackOutput->MemoryBase = ds->context->X10; ds->state++; break; | |
| 58 case 11: CallbackOutput->MemoryBase = ds->context->X11; ds->state++; break; | |
| 59 case 12: CallbackOutput->MemoryBase = ds->context->X12; ds->state++; break; | |
| 60 #ifndef _M_ARM64EC | |
| 61 case 13: CallbackOutput->MemoryBase = ds->context->X13; ds->state++; break; | |
| 62 case 14: CallbackOutput->MemoryBase = ds->context->X14; ds->state++; break; | |
| 63 case 15: CallbackOutput->MemoryBase = ds->context->X15; ds->state++; break; | |
| 64 case 16: CallbackOutput->MemoryBase = ds->context->X16; ds->state++; break; | |
| 65 case 17: CallbackOutput->MemoryBase = ds->context->X17; ds->state++; break; | |
| 66 case 18: CallbackOutput->MemoryBase = ds->context->X18; ds->state++; break; | |
| 67 case 19: CallbackOutput->MemoryBase = ds->context->X19; ds->state++; break; | |
| 68 case 20: CallbackOutput->MemoryBase = ds->context->X20; ds->state++; break; | |
| 69 case 21: CallbackOutput->MemoryBase = ds->context->X21; ds->state++; break; | |
| 70 case 22: CallbackOutput->MemoryBase = ds->context->X22; ds->state++; break; | |
| 71 case 23: CallbackOutput->MemoryBase = ds->context->X23; ds->state++; break; | |
| 72 case 24: CallbackOutput->MemoryBase = ds->context->X24; ds->state++; break; | |
| 73 case 25: CallbackOutput->MemoryBase = ds->context->X25; ds->state++; break; | |
| 74 case 26: CallbackOutput->MemoryBase = ds->context->X26; ds->state++; break; | |
| 75 case 27: CallbackOutput->MemoryBase = ds->context->X27; ds->state++; break; | |
| 76 case 28: CallbackOutput->MemoryBase = ds->context->X28; ds->state++; break; | |
| 77 #endif | |
| 78 | |
| 79 // Save memory referenced by values in stack. | |
| 80 default: | |
| 81 if (ds->state < 0x1000) | |
| 82 ds->state = 0x1000; | |
| 83 | |
| 84 size_t addr; | |
| 85 do { | |
| 86 if (ds->state > 0x1000 + STACK_SEARCH_SIZE) | |
| 87 return FALSE; | |
| 88 | |
| 89 if (!safeRead((void*)((ds->context->Sp & ~7) + ds->state - 0x1000), addr)) | |
| 90 return FALSE; | |
| 91 | |
| 92 ds->state += 4; | |
| 93 } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); | |
| 94 | |
| 95 CallbackOutput->MemoryBase = addr; | |
| 96 break; | |
| 97 } | |
| 98 | |
| 99 if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) | |
| 100 CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; | |
| 101 CallbackOutput->MemorySize = MEM_BLOCK_SIZE; | |
| 102 | |
| 103 // No need to perform additional checks here, the minidump engine | |
| 104 // safely clips the addresses to valid memory pages only. | |
| 105 // Also seems to apply for overlapped areas etc. | |
| 106 } | |
| 107 | |
| 108 return TRUE; | |
| 109 } | |
| 110 | |
| 111 #elif defined(_M_IX86) | |
| 112 | |
| 113 BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, | |
| 114 const PMINIDUMP_CALLBACK_INPUT CallbackInput, | |
| 115 PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) | |
| 116 { | |
| 117 static const unsigned STACK_SEARCH_SIZE = 0x400; | |
| 118 static const unsigned MEM_BLOCK_SIZE = 0x400; | |
| 119 | |
| 120 if (CallbackInput->CallbackType == MemoryCallback) { | |
| 121 // Called to get user defined blocks of memory to write until | |
| 122 // callback returns FALSE or CallbackOutput->MemorySize == 0. | |
| 123 | |
| 124 DumpState* ds = (DumpState*)CallbackParam; | |
| 125 switch (ds->state) { | |
| 126 // Save memory referenced by registers. | |
| 127 case 0: CallbackOutput->MemoryBase = ds->context->Eax; ds->state++; break; | |
| 128 case 1: CallbackOutput->MemoryBase = ds->context->Ebx; ds->state++; break; | |
| 129 case 2: CallbackOutput->MemoryBase = ds->context->Ecx; ds->state++; break; | |
| 130 case 3: CallbackOutput->MemoryBase = ds->context->Edx; ds->state++; break; | |
| 131 case 4: CallbackOutput->MemoryBase = ds->context->Esi; ds->state++; break; | |
| 132 case 5: CallbackOutput->MemoryBase = ds->context->Edi; ds->state++; break; | |
| 133 case 6: CallbackOutput->MemoryBase = ds->context->Ebp; ds->state++; break; | |
| 134 case 7: CallbackOutput->MemoryBase = ds->context->Esp; ds->state++; break; | |
| 135 case 8: CallbackOutput->MemoryBase = ds->context->Eip; ds->state++; break; | |
| 136 | |
| 137 // Save memory referenced by values in stack. | |
| 138 default: | |
| 139 if (ds->state < 0x1000) | |
| 140 ds->state = 0x1000; | |
| 141 | |
| 142 size_t addr; | |
| 143 do { | |
| 144 if (ds->state > 0x1000 + STACK_SEARCH_SIZE) | |
| 145 return FALSE; | |
| 146 | |
| 147 if (!safeRead((void*)((ds->context->Esp & ~3) + ds->state - 0x1000), addr)) | |
| 148 return FALSE; | |
| 149 | |
| 150 ds->state += 4; | |
| 151 } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); | |
| 152 | |
| 153 CallbackOutput->MemoryBase = addr; | |
| 154 break; | |
| 155 } | |
| 156 | |
| 157 if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) | |
| 158 CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; | |
| 159 CallbackOutput->MemorySize = MEM_BLOCK_SIZE; | |
| 160 | |
| 161 // No need to perform additional checks here, the minidump engine | |
| 162 // safely clips the addresses to valid memory pages only. | |
| 163 // Also seems to apply for overlapped areas etc. | |
| 164 } | |
| 165 | |
| 166 return TRUE; | |
| 167 } | |
| 168 | |
| 169 #elif defined(_M_X64) | |
| 170 | |
| 171 BOOL WINAPI MiniDumpCallback(PVOID CallbackParam, | |
| 172 const PMINIDUMP_CALLBACK_INPUT CallbackInput, | |
| 173 PMINIDUMP_CALLBACK_OUTPUT CallbackOutput) | |
| 174 { | |
| 175 static const unsigned STACK_SEARCH_SIZE = 0x400; | |
| 176 static const unsigned MEM_BLOCK_SIZE = 0x400; | |
| 177 | |
| 178 if (CallbackInput->CallbackType == MemoryCallback) { | |
| 179 // Called to get user defined blocks of memory to write until | |
| 180 // callback returns FALSE or CallbackOutput->MemorySize == 0. | |
| 181 | |
| 182 DumpState* ds = (DumpState*)CallbackParam; | |
| 183 switch (ds->state) { | |
| 184 // Save memory referenced by registers. | |
| 185 case 0: CallbackOutput->MemoryBase = ds->context->Rax; ds->state++; break; | |
| 186 case 1: CallbackOutput->MemoryBase = ds->context->Rbx; ds->state++; break; | |
| 187 case 2: CallbackOutput->MemoryBase = ds->context->Rcx; ds->state++; break; | |
| 188 case 3: CallbackOutput->MemoryBase = ds->context->Rdx; ds->state++; break; | |
| 189 case 4: CallbackOutput->MemoryBase = ds->context->Rsi; ds->state++; break; | |
| 190 case 5: CallbackOutput->MemoryBase = ds->context->Rdi; ds->state++; break; | |
| 191 case 6: CallbackOutput->MemoryBase = ds->context->Rsp; ds->state++; break; | |
| 192 case 7: CallbackOutput->MemoryBase = ds->context->Rbp; ds->state++; break; | |
| 193 case 8: CallbackOutput->MemoryBase = ds->context->R8; ds->state++; break; | |
| 194 case 9: CallbackOutput->MemoryBase = ds->context->R9; ds->state++; break; | |
| 195 case 10: CallbackOutput->MemoryBase = ds->context->R10; ds->state++; break; | |
| 196 case 11: CallbackOutput->MemoryBase = ds->context->R11; ds->state++; break; | |
| 197 case 12: CallbackOutput->MemoryBase = ds->context->R12; ds->state++; break; | |
| 198 case 13: CallbackOutput->MemoryBase = ds->context->R13; ds->state++; break; | |
| 199 case 14: CallbackOutput->MemoryBase = ds->context->R14; ds->state++; break; | |
| 200 case 15: CallbackOutput->MemoryBase = ds->context->R15; ds->state++; break; | |
| 201 | |
| 202 // Save memory referenced by values in stack. | |
| 203 default: | |
| 204 if (ds->state < 0x1000) | |
| 205 ds->state = 0x1000; | |
| 206 | |
| 207 size_t addr; | |
| 208 do { | |
| 209 if (ds->state > 0x1000 + STACK_SEARCH_SIZE) | |
| 210 return FALSE; | |
| 211 | |
| 212 if (!safeRead((void*)((ds->context->Rsp & ~7) + ds->state - 0x1000), addr)) | |
| 213 return FALSE; | |
| 214 | |
| 215 ds->state += 4; | |
| 216 } while (addr < 0x1000 || !safeTestReadAccess((void*)addr)); | |
| 217 | |
| 218 CallbackOutput->MemoryBase = addr; | |
| 219 break; | |
| 220 } | |
| 221 | |
| 222 if (CallbackOutput->MemoryBase >= MEM_BLOCK_SIZE / 2) | |
| 223 CallbackOutput->MemoryBase -= MEM_BLOCK_SIZE / 2; | |
| 224 CallbackOutput->MemorySize = MEM_BLOCK_SIZE; | |
| 225 | |
| 226 // No need to perform additional checks here, the minidump engine | |
| 227 // safely clips the addresses to valid memory pages only. | |
| 228 // Also seems to apply for overlapped areas etc. | |
| 229 } | |
| 230 | |
| 231 return TRUE; | |
| 232 } | |
| 233 | |
| 234 #endif // _M_X64 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | |
| 239 BOOL WriteMiniDumpHelper(HANDLE hDump, LPEXCEPTION_POINTERS param) { | |
| 240 MINIDUMP_EXCEPTION_INFORMATION exception = {}; | |
| 241 exception.ThreadId = GetCurrentThreadId(); | |
| 242 exception.ExceptionPointers = param; | |
| 243 exception.ClientPointers = FALSE; | |
| 244 DumpState ds; | |
| 245 ds.state = 0; | |
| 246 ds.context = reinterpret_cast<myCONTEXT*>(param->ContextRecord); | |
| 247 MINIDUMP_CALLBACK_INFORMATION mci; | |
| 248 mci.CallbackRoutine = &MiniDumpCallback; | |
| 249 mci.CallbackParam = (void*)&ds; | |
| 250 return MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, (MINIDUMP_TYPE)(MiniDumpWithUnloadedModules), &exception, NULL, &mci); | |
| 251 } |
