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 }