user32: Propagate the LoadImage flags into the low-level loader functions.
[wine.git] / dlls / dbghelp / minidump.c
blobfd7eb1ace4ebe0ee0c562ef88348366ac1a9ae31
1 /*
2 * File minidump.c - management of dumps (read & write)
4 * Copyright (C) 2004-2005, Eric Pouech
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <time.h>
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "dbghelp_private.h"
29 #include "winternl.h"
30 #include "psapi.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
35 struct dump_memory
37 ULONG64 base;
38 ULONG size;
39 ULONG rva;
42 struct dump_module
44 unsigned is_elf;
45 ULONG64 base;
46 ULONG size;
47 DWORD timestamp;
48 DWORD checksum;
49 WCHAR name[MAX_PATH];
52 struct dump_context
54 /* process & thread information */
55 HANDLE hProcess;
56 DWORD pid;
57 void* pcs_buffer;
58 SYSTEM_PROCESS_INFORMATION* spi;
59 /* module information */
60 struct dump_module* modules;
61 unsigned num_modules;
62 unsigned alloc_modules;
63 /* exception information */
64 /* output information */
65 MINIDUMP_TYPE type;
66 HANDLE hFile;
67 RVA rva;
68 struct dump_memory* mem;
69 unsigned num_mem;
70 unsigned alloc_mem;
71 /* callback information */
72 MINIDUMP_CALLBACK_INFORMATION* cb;
75 /******************************************************************
76 * fetch_processes_info
78 * reads system wide process information, and make spi point to the record
79 * for process of id 'pid'
81 static BOOL fetch_processes_info(struct dump_context* dc)
83 ULONG buf_size = 0x1000;
84 NTSTATUS nts;
86 dc->pcs_buffer = NULL;
87 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
88 for (;;)
90 nts = NtQuerySystemInformation(SystemProcessInformation,
91 dc->pcs_buffer, buf_size, NULL);
92 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
93 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
94 buf_size *= 2);
95 if (!dc->pcs_buffer) return FALSE;
98 if (nts == STATUS_SUCCESS)
100 dc->spi = dc->pcs_buffer;
101 for (;;)
103 if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
104 if (!dc->spi->NextEntryOffset) break;
105 dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
108 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
109 dc->pcs_buffer = NULL;
110 dc->spi = NULL;
111 return FALSE;
114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
115 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
117 NT_TIB tib;
118 ADDRESS64 addr;
120 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL) &&
121 dbghelp_current_cpu &&
122 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
124 if (addr.Offset)
126 addr.Offset -= dbghelp_current_cpu->word_size;
127 /* make sure stack pointer is within the established range of the stack. It could have
128 been clobbered by whatever caused the original exception. */
129 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
130 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
132 else
133 mmd->StartOfMemoryRange = addr.Offset;
135 else
136 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
137 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
141 /******************************************************************
142 * fetch_thread_info
144 * fetches some information about thread of id 'tid'
146 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
147 const MINIDUMP_EXCEPTION_INFORMATION* except,
148 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
150 DWORD tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
151 HANDLE hThread;
152 THREAD_BASIC_INFORMATION tbi;
154 memset(ctx, 0, sizeof(*ctx));
156 mdThd->ThreadId = tid;
157 mdThd->SuspendCount = 0;
158 mdThd->Teb = 0;
159 mdThd->Stack.StartOfMemoryRange = 0;
160 mdThd->Stack.Memory.DataSize = 0;
161 mdThd->Stack.Memory.Rva = 0;
162 mdThd->ThreadContext.DataSize = 0;
163 mdThd->ThreadContext.Rva = 0;
164 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
165 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
167 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
169 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
170 return FALSE;
173 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
174 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
176 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
177 if (tbi.ExitStatus == STILL_ACTIVE)
179 if (tid != GetCurrentThreadId() &&
180 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
182 ctx->ContextFlags = CONTEXT_FULL;
183 if (!GetThreadContext(hThread, ctx))
184 memset(ctx, 0, sizeof(*ctx));
186 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
187 ResumeThread(hThread);
189 else if (tid == GetCurrentThreadId() && except)
191 CONTEXT lctx, *pctx;
192 mdThd->SuspendCount = 1;
193 if (except->ClientPointers)
195 EXCEPTION_POINTERS ep;
197 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
198 &ep, sizeof(ep), NULL);
199 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
200 &lctx, sizeof(lctx), NULL);
201 pctx = &lctx;
203 else pctx = except->ExceptionPointers->ContextRecord;
205 *ctx = *pctx;
206 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
208 else mdThd->SuspendCount = 0;
211 CloseHandle(hThread);
212 return TRUE;
215 /******************************************************************
216 * add_module
218 * Add a module to a dump context
220 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
221 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
222 BOOL is_elf)
224 if (!dc->modules)
226 dc->alloc_modules = 32;
227 dc->modules = HeapAlloc(GetProcessHeap(), 0,
228 dc->alloc_modules * sizeof(*dc->modules));
230 else if(dc->num_modules >= dc->alloc_modules)
232 dc->alloc_modules *= 2;
233 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
234 dc->alloc_modules * sizeof(*dc->modules));
236 if (!dc->modules)
238 dc->alloc_modules = dc->num_modules = 0;
239 return FALSE;
241 if (is_elf ||
242 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base,
243 dc->modules[dc->num_modules].name,
244 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)))
245 lstrcpynW(dc->modules[dc->num_modules].name, name,
246 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR));
247 dc->modules[dc->num_modules].base = base;
248 dc->modules[dc->num_modules].size = size;
249 dc->modules[dc->num_modules].timestamp = timestamp;
250 dc->modules[dc->num_modules].checksum = checksum;
251 dc->modules[dc->num_modules].is_elf = is_elf;
252 dc->num_modules++;
254 return TRUE;
257 /******************************************************************
258 * fetch_pe_module_info_cb
260 * Callback for accumulating in dump_context a PE modules set
262 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
263 PVOID user)
265 struct dump_context* dc = user;
266 IMAGE_NT_HEADERS nth;
268 if (!validate_addr64(base)) return FALSE;
270 if (pe_load_nt_header(dc->hProcess, base, &nth))
271 add_module(user, name, base, size,
272 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
273 FALSE);
274 return TRUE;
277 /******************************************************************
278 * fetch_elf_module_info_cb
280 * Callback for accumulating in dump_context an ELF modules set
282 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
283 void* user)
285 struct dump_context* dc = user;
286 DWORD_PTR rbase;
287 DWORD size, checksum;
289 /* FIXME: there's no relevant timestamp on ELF modules */
290 /* NB: if we have a non-null base from the live-target use it (whenever
291 * the ELF module is relocatable or not). If we have a null base (ELF
292 * module isn't relocatable) then grab its base address from ELF file
294 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
295 size = checksum = 0;
296 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
297 return TRUE;
300 /******************************************************************
301 * fetch_macho_module_info_cb
303 * Callback for accumulating in dump_context a Mach-O modules set
305 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
306 void* user)
308 struct dump_context* dc = (struct dump_context*)user;
309 DWORD_PTR rbase;
310 DWORD size, checksum;
312 /* FIXME: there's no relevant timestamp on Mach-O modules */
313 /* NB: if we have a non-null base from the live-target use it. If we have
314 * a null base, then grab its base address from Mach-O file.
316 if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
317 size = checksum = 0;
318 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
319 return TRUE;
322 static void fetch_modules_info(struct dump_context* dc)
324 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
325 /* Since we include ELF modules in a separate stream from the regular PE ones,
326 * we can always include those ELF modules (they don't eat lots of space)
327 * And it's always a good idea to have a trace of the loaded ELF modules for
328 * a given application in a post mortem debugging condition.
330 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
331 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
334 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
336 DWORD handle;
337 DWORD sz;
338 static const WCHAR backslashW[] = {'\\', '\0'};
340 memset(ffi, 0, sizeof(*ffi));
341 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
343 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
344 if (info && GetFileVersionInfoW(filename, handle, sz, info))
346 VS_FIXEDFILEINFO* ptr;
347 UINT len;
349 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
350 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
352 HeapFree(GetProcessHeap(), 0, info);
356 /******************************************************************
357 * add_memory_block
359 * Add a memory block to be dumped in a minidump
360 * If rva is non 0, it's the rva in the minidump where has to be stored
361 * also the rva of the memory block when written (this allows to reference
362 * a memory block from outside the list of memory blocks).
364 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
366 if (!dc->mem)
368 dc->alloc_mem = 32;
369 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
371 else if (dc->num_mem >= dc->alloc_mem)
373 dc->alloc_mem *= 2;
374 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
375 dc->alloc_mem * sizeof(*dc->mem));
377 if (dc->mem)
379 dc->mem[dc->num_mem].base = base;
380 dc->mem[dc->num_mem].size = size;
381 dc->mem[dc->num_mem].rva = rva;
382 dc->num_mem++;
384 else dc->num_mem = dc->alloc_mem = 0;
387 /******************************************************************
388 * writeat
390 * Writes a chunk of data at a given position in the minidump
392 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
394 DWORD written;
396 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
397 WriteFile(dc->hFile, data, size, &written, NULL);
400 /******************************************************************
401 * append
403 * writes a new chunk of data to the minidump, increasing the current
404 * rva in dc
406 static void append(struct dump_context* dc, const void* data, unsigned size)
408 writeat(dc, dc->rva, data, size);
409 dc->rva += size;
412 /******************************************************************
413 * dump_exception_info
415 * Write in File the exception information from pcs
417 static unsigned dump_exception_info(struct dump_context* dc,
418 const MINIDUMP_EXCEPTION_INFORMATION* except)
420 MINIDUMP_EXCEPTION_STREAM mdExcpt;
421 EXCEPTION_RECORD rec, *prec;
422 CONTEXT ctx, *pctx;
423 DWORD i;
425 mdExcpt.ThreadId = except->ThreadId;
426 mdExcpt.__alignment = 0;
427 if (except->ClientPointers)
429 EXCEPTION_POINTERS ep;
431 ReadProcessMemory(dc->hProcess,
432 except->ExceptionPointers, &ep, sizeof(ep), NULL);
433 ReadProcessMemory(dc->hProcess,
434 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
435 ReadProcessMemory(dc->hProcess,
436 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
437 prec = &rec;
438 pctx = &ctx;
440 else
442 prec = except->ExceptionPointers->ExceptionRecord;
443 pctx = except->ExceptionPointers->ContextRecord;
445 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
446 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
447 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
448 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
449 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
450 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
451 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
452 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
453 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
454 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
456 append(dc, &mdExcpt, sizeof(mdExcpt));
457 append(dc, pctx, sizeof(*pctx));
458 return sizeof(mdExcpt);
461 /******************************************************************
462 * dump_modules
464 * Write in File the modules from pcs
466 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
468 MINIDUMP_MODULE mdModule;
469 MINIDUMP_MODULE_LIST mdModuleList;
470 char tmp[1024];
471 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
472 ULONG i, nmod;
473 RVA rva_base;
474 DWORD flags_out;
475 unsigned sz;
477 for (i = nmod = 0; i < dc->num_modules; i++)
479 if ((dc->modules[i].is_elf && dump_elf) ||
480 (!dc->modules[i].is_elf && !dump_elf))
481 nmod++;
484 mdModuleList.NumberOfModules = 0;
485 /* reserve space for mdModuleList
486 * FIXME: since we don't support 0 length arrays, we cannot use the
487 * size of mdModuleList
488 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
491 /* the stream size is just the size of the module index. It does not include the data for the
492 names of each module. *Technically* the names are supposed to go into the common string table
493 in the minidump file. Since each string is referenced by RVA they can all safely be located
494 anywhere between streams in the file, so the end of this stream is sufficient. */
495 rva_base = dc->rva;
496 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
497 for (i = 0; i < dc->num_modules; i++)
499 if ((dc->modules[i].is_elf && !dump_elf) ||
500 (!dc->modules[i].is_elf && dump_elf))
501 continue;
503 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
504 if (dc->type & MiniDumpWithDataSegs)
505 flags_out |= ModuleWriteDataSeg;
506 if (dc->type & MiniDumpWithProcessThreadData)
507 flags_out |= ModuleWriteTlsData;
508 if (dc->type & MiniDumpWithCodeSegs)
509 flags_out |= ModuleWriteCodeSegs;
510 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
511 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
512 FIXME("Buffer overflow!!!\n");
513 lstrcpyW(ms->Buffer, dc->modules[i].name);
515 if (dc->cb)
517 MINIDUMP_CALLBACK_INPUT cbin;
518 MINIDUMP_CALLBACK_OUTPUT cbout;
520 cbin.ProcessId = dc->pid;
521 cbin.ProcessHandle = dc->hProcess;
522 cbin.CallbackType = ModuleCallback;
524 cbin.u.Module.FullPath = ms->Buffer;
525 cbin.u.Module.BaseOfImage = dc->modules[i].base;
526 cbin.u.Module.SizeOfImage = dc->modules[i].size;
527 cbin.u.Module.CheckSum = dc->modules[i].checksum;
528 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
529 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
530 cbin.u.Module.CvRecord = NULL;
531 cbin.u.Module.SizeOfCvRecord = 0;
532 cbin.u.Module.MiscRecord = NULL;
533 cbin.u.Module.SizeOfMiscRecord = 0;
535 cbout.u.ModuleWriteFlags = flags_out;
536 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
537 continue;
538 flags_out &= cbout.u.ModuleWriteFlags;
540 if (flags_out & ModuleWriteModule)
542 mdModule.BaseOfImage = dc->modules[i].base;
543 mdModule.SizeOfImage = dc->modules[i].size;
544 mdModule.CheckSum = dc->modules[i].checksum;
545 mdModule.TimeDateStamp = dc->modules[i].timestamp;
546 mdModule.ModuleNameRva = dc->rva;
547 ms->Length -= sizeof(WCHAR);
548 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
549 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
550 mdModule.CvRecord.DataSize = 0; /* FIXME */
551 mdModule.CvRecord.Rva = 0; /* FIXME */
552 mdModule.MiscRecord.DataSize = 0; /* FIXME */
553 mdModule.MiscRecord.Rva = 0; /* FIXME */
554 mdModule.Reserved0 = 0; /* FIXME */
555 mdModule.Reserved1 = 0; /* FIXME */
556 writeat(dc,
557 rva_base + sizeof(mdModuleList.NumberOfModules) +
558 mdModuleList.NumberOfModules++ * sizeof(mdModule),
559 &mdModule, sizeof(mdModule));
562 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
563 sizeof(mdModuleList.NumberOfModules));
565 return sz;
568 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
569 * We are compiled with -fPIC, so we can't clobber ebx.
571 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
573 #if defined(__GNUC__) && defined(__i386__)
574 __asm__("pushl %%ebx\n\t"
575 "cpuid\n\t"
576 "movl %%ebx, %%esi\n\t"
577 "popl %%ebx"
578 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
579 : "0" (ax));
580 #endif
583 /* From xf86info havecpuid.c 1.11 */
584 static inline int have_x86cpuid(void)
586 #if defined(__GNUC__) && defined(__i386__)
587 unsigned int f1, f2;
588 __asm__("pushfl\n\t"
589 "pushfl\n\t"
590 "popl %0\n\t"
591 "movl %0,%1\n\t"
592 "xorl %2,%0\n\t"
593 "pushl %0\n\t"
594 "popfl\n\t"
595 "pushfl\n\t"
596 "popl %0\n\t"
597 "popfl"
598 : "=&r" (f1), "=&r" (f2)
599 : "ir" (0x00200000));
600 return ((f1^f2) & 0x00200000) != 0;
601 #else
602 return 0;
603 #endif
606 /******************************************************************
607 * dump_system_info
609 * Dumps into File the information about the system
611 static unsigned dump_system_info(struct dump_context* dc)
613 MINIDUMP_SYSTEM_INFO mdSysInfo;
614 SYSTEM_INFO sysInfo;
615 OSVERSIONINFOW osInfo;
616 DWORD written;
617 ULONG slen;
619 GetSystemInfo(&sysInfo);
620 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
621 GetVersionExW(&osInfo);
623 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
624 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
625 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
626 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
627 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
628 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
629 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
630 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
631 mdSysInfo.PlatformId = osInfo.dwPlatformId;
633 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
634 mdSysInfo.u1.Reserved1 = 0;
635 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
637 if (have_x86cpuid())
639 unsigned regs0[4], regs1[4];
641 do_x86cpuid(0, regs0);
642 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
643 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
644 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
645 do_x86cpuid(1, regs1);
646 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
647 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
648 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
649 if (regs0[1] == 0x68747541 /* "Auth" */ &&
650 regs0[3] == 0x69746e65 /* "enti" */ &&
651 regs0[2] == 0x444d4163 /* "cAMD" */)
653 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
654 if (regs1[0] >= 0x80000001)
656 do_x86cpuid(0x80000001, regs1); /* get vendor features */
657 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
661 else
663 unsigned i;
664 ULONG64 one = 1;
666 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
667 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
669 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
670 if (IsProcessorFeaturePresent(i))
671 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
673 append(dc, &mdSysInfo, sizeof(mdSysInfo));
675 /* write the service pack version string after this stream. It is referenced within the
676 stream by its RVA in the file. */
677 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
678 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
679 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
680 dc->rva += sizeof(ULONG) + slen;
682 return sizeof(mdSysInfo);
685 /******************************************************************
686 * dump_threads
688 * Dumps into File the information about running threads
690 static unsigned dump_threads(struct dump_context* dc,
691 const MINIDUMP_EXCEPTION_INFORMATION* except)
693 MINIDUMP_THREAD mdThd;
694 MINIDUMP_THREAD_LIST mdThdList;
695 unsigned i, sz;
696 RVA rva_base;
697 DWORD flags_out;
698 CONTEXT ctx;
700 mdThdList.NumberOfThreads = 0;
702 rva_base = dc->rva;
703 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->spi->dwThreadCount * sizeof(mdThd);
705 for (i = 0; i < dc->spi->dwThreadCount; i++)
707 fetch_thread_info(dc, i, except, &mdThd, &ctx);
709 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
710 ThreadWriteInstructionWindow;
711 if (dc->type & MiniDumpWithProcessThreadData)
712 flags_out |= ThreadWriteThreadData;
713 if (dc->type & MiniDumpWithThreadInfo)
714 flags_out |= ThreadWriteThreadInfo;
716 if (dc->cb)
718 MINIDUMP_CALLBACK_INPUT cbin;
719 MINIDUMP_CALLBACK_OUTPUT cbout;
721 cbin.ProcessId = dc->pid;
722 cbin.ProcessHandle = dc->hProcess;
723 cbin.CallbackType = ThreadCallback;
724 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
725 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
726 cbin.u.Thread.Context = ctx;
727 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
728 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
729 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
730 mdThd.Stack.Memory.DataSize;
732 cbout.u.ThreadWriteFlags = flags_out;
733 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
734 continue;
735 flags_out &= cbout.u.ThreadWriteFlags;
737 if (flags_out & ThreadWriteThread)
739 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
741 mdThd.ThreadContext.Rva = dc->rva;
742 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
743 append(dc, &ctx, sizeof(CONTEXT));
745 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
747 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
748 mdThd.Stack.Memory.DataSize,
749 rva_base + sizeof(mdThdList.NumberOfThreads) +
750 mdThdList.NumberOfThreads * sizeof(mdThd) +
751 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
753 writeat(dc,
754 rva_base + sizeof(mdThdList.NumberOfThreads) +
755 mdThdList.NumberOfThreads * sizeof(mdThd),
756 &mdThd, sizeof(mdThd));
757 mdThdList.NumberOfThreads++;
759 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
761 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
762 * - also crop values across module boundaries,
763 * - and don't make it i386 dependent
765 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
768 writeat(dc, rva_base,
769 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
771 return sz;
774 /******************************************************************
775 * dump_memory_info
777 * dumps information about the memory of the process (stack of the threads)
779 static unsigned dump_memory_info(struct dump_context* dc)
781 MINIDUMP_MEMORY_LIST mdMemList;
782 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
783 DWORD written;
784 unsigned i, pos, len, sz;
785 RVA rva_base;
786 char tmp[1024];
788 mdMemList.NumberOfMemoryRanges = dc->num_mem;
789 append(dc, &mdMemList.NumberOfMemoryRanges,
790 sizeof(mdMemList.NumberOfMemoryRanges));
791 rva_base = dc->rva;
792 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
793 dc->rva += sz;
794 sz += sizeof(mdMemList.NumberOfMemoryRanges);
796 for (i = 0; i < dc->num_mem; i++)
798 mdMem.StartOfMemoryRange = dc->mem[i].base;
799 mdMem.Memory.Rva = dc->rva;
800 mdMem.Memory.DataSize = dc->mem[i].size;
801 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
802 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
804 len = min(dc->mem[i].size - pos, sizeof(tmp));
805 if (ReadProcessMemory(dc->hProcess,
806 (void*)(DWORD_PTR)(dc->mem[i].base + pos),
807 tmp, len, NULL))
808 WriteFile(dc->hFile, tmp, len, &written, NULL);
810 dc->rva += mdMem.Memory.DataSize;
811 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
812 if (dc->mem[i].rva)
814 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
818 return sz;
821 static unsigned dump_misc_info(struct dump_context* dc)
823 MINIDUMP_MISC_INFO mmi;
825 mmi.SizeOfInfo = sizeof(mmi);
826 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
827 mmi.ProcessId = dc->pid;
828 /* FIXME: create/user/kernel time */
829 mmi.ProcessCreateTime = 0;
830 mmi.ProcessKernelTime = 0;
831 mmi.ProcessUserTime = 0;
833 append(dc, &mmi, sizeof(mmi));
834 return sizeof(mmi);
837 /******************************************************************
838 * MiniDumpWriteDump (DEBUGHLP.@)
841 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
842 MINIDUMP_TYPE DumpType,
843 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
844 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
845 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
847 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
848 MINIDUMP_HEADER mdHead;
849 MINIDUMP_DIRECTORY mdDir;
850 DWORD i, nStreams, idx_stream;
851 struct dump_context dc;
853 dc.hProcess = hProcess;
854 dc.hFile = hFile;
855 dc.pid = pid;
856 dc.modules = NULL;
857 dc.num_modules = 0;
858 dc.alloc_modules = 0;
859 dc.cb = CallbackParam;
860 dc.type = DumpType;
861 dc.mem = NULL;
862 dc.num_mem = 0;
863 dc.alloc_mem = 0;
864 dc.rva = 0;
866 if (!fetch_processes_info(&dc)) return FALSE;
867 fetch_modules_info(&dc);
869 /* 1) init */
870 nStreams = 6 + (ExceptionParam ? 1 : 0) +
871 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
873 /* pad the directory size to a multiple of 4 for alignment purposes */
874 nStreams = (nStreams + 3) & ~3;
876 if (DumpType & MiniDumpWithDataSegs)
877 FIXME("NIY MiniDumpWithDataSegs\n");
878 if (DumpType & MiniDumpWithFullMemory)
879 FIXME("NIY MiniDumpWithFullMemory\n");
880 if (DumpType & MiniDumpWithHandleData)
881 FIXME("NIY MiniDumpWithHandleData\n");
882 if (DumpType & MiniDumpFilterMemory)
883 FIXME("NIY MiniDumpFilterMemory\n");
884 if (DumpType & MiniDumpScanMemory)
885 FIXME("NIY MiniDumpScanMemory\n");
887 /* 2) write header */
888 mdHead.Signature = MINIDUMP_SIGNATURE;
889 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
890 mdHead.NumberOfStreams = nStreams;
891 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
892 mdHead.StreamDirectoryRva = sizeof(mdHead);
893 mdHead.u.TimeDateStamp = time(NULL);
894 mdHead.Flags = DumpType;
895 append(&dc, &mdHead, sizeof(mdHead));
897 /* 3) write stream directories */
898 dc.rva += nStreams * sizeof(mdDir);
899 idx_stream = 0;
901 /* 3.1) write data stream directories */
903 /* must be first in minidump */
904 mdDir.StreamType = SystemInfoStream;
905 mdDir.Location.Rva = dc.rva;
906 mdDir.Location.DataSize = dump_system_info(&dc);
907 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
908 &mdDir, sizeof(mdDir));
910 mdDir.StreamType = ThreadListStream;
911 mdDir.Location.Rva = dc.rva;
912 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
913 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
914 &mdDir, sizeof(mdDir));
916 mdDir.StreamType = ModuleListStream;
917 mdDir.Location.Rva = dc.rva;
918 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
919 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
920 &mdDir, sizeof(mdDir));
922 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
923 mdDir.Location.Rva = dc.rva;
924 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
925 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
926 &mdDir, sizeof(mdDir));
928 mdDir.StreamType = MemoryListStream;
929 mdDir.Location.Rva = dc.rva;
930 mdDir.Location.DataSize = dump_memory_info(&dc);
931 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
932 &mdDir, sizeof(mdDir));
934 mdDir.StreamType = MiscInfoStream;
935 mdDir.Location.Rva = dc.rva;
936 mdDir.Location.DataSize = dump_misc_info(&dc);
937 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
938 &mdDir, sizeof(mdDir));
940 /* 3.2) write exception information (if any) */
941 if (ExceptionParam)
943 mdDir.StreamType = ExceptionStream;
944 mdDir.Location.Rva = dc.rva;
945 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
946 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
947 &mdDir, sizeof(mdDir));
950 /* 3.3) write user defined streams (if any) */
951 if (UserStreamParam)
953 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
955 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
956 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
957 mdDir.Location.Rva = dc.rva;
958 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
959 &mdDir, sizeof(mdDir));
960 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
961 UserStreamParam->UserStreamArray[i].BufferSize);
965 /* fill the remaining directory entries with 0's (unused stream types) */
966 /* NOTE: this should always come last in the dump! */
967 for (i = idx_stream; i < nStreams; i++)
968 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
970 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
971 HeapFree(GetProcessHeap(), 0, dc.mem);
972 HeapFree(GetProcessHeap(), 0, dc.modules);
974 return TRUE;
977 /******************************************************************
978 * MiniDumpReadDumpStream (DEBUGHLP.@)
982 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
983 PMINIDUMP_DIRECTORY* pdir,
984 PVOID* stream, ULONG* size)
986 MINIDUMP_HEADER* mdHead = base;
988 if (mdHead->Signature == MINIDUMP_SIGNATURE)
990 MINIDUMP_DIRECTORY* dir;
991 DWORD i;
993 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
994 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
996 if (dir->StreamType == str_idx)
998 if (pdir) *pdir = dir;
999 if (stream) *stream = (char*)base + dir->Location.Rva;
1000 if (size) *size = dir->Location.DataSize;
1001 return TRUE;
1005 SetLastError(ERROR_INVALID_PARAMETER);
1006 return FALSE;