xref: /reactos/dll/win32/dbghelp/minidump.c (revision c8d07514)
1 /*
2  * File minidump.c - management of dumps (read & write)
3  *
4  * Copyright (C) 2004-2005, Eric Pouech
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #include <time.h>
22 
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25 
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/asm.h"
32 #include "wine/debug.h"
33 
34 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
35 
36 /******************************************************************
37  *		fetch_process_info
38  *
39  * reads system wide process information, and gather from it the threads information
40  * for process of id 'pid'
41  */
42 static BOOL fetch_process_info(struct dump_context* dc)
43 {
44     ULONG       buf_size = 0x1000;
45     NTSTATUS    nts;
46     void*       pcs_buffer = NULL;
47 
48     if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
49     for (;;)
50     {
51         nts = NtQuerySystemInformation(SystemProcessInformation,
52                                        pcs_buffer, buf_size, NULL);
53         if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
54         pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2);
55         if (!pcs_buffer) return FALSE;
56     }
57 
58     if (nts == STATUS_SUCCESS)
59     {
60         SYSTEM_PROCESS_INFORMATION*     spi = pcs_buffer;
61         unsigned                        i;
62 
63         for (;;)
64         {
65             if (HandleToUlong(spi->UniqueProcessId) == dc->pid)
66             {
67                 dc->num_threads = spi->dwThreadCount;
68                 dc->threads = HeapAlloc(GetProcessHeap(), 0,
69                                         dc->num_threads * sizeof(dc->threads[0]));
70                 if (!dc->threads) goto failed;
71                 for (i = 0; i < dc->num_threads; i++)
72                 {
73                     dc->threads[i].tid        = HandleToULong(spi->ti[i].ClientId.UniqueThread);
74                     dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */
75                     dc->threads[i].curr_prio  = spi->ti[i].dwCurrentPriority;
76                 }
77                 HeapFree(GetProcessHeap(), 0, pcs_buffer);
78                 return TRUE;
79             }
80             if (!spi->NextEntryOffset) break;
81             spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset);
82         }
83     }
84 failed:
85     HeapFree(GetProcessHeap(), 0, pcs_buffer);
86     return FALSE;
87 }
88 
89 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
90                                const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
91 {
92     NT_TIB      tib;
93     ADDRESS64   addr;
94 
95     if (ReadProcessMemory(dc->process->handle, teb_addr, &tib, sizeof(tib), NULL) &&
96         dbghelp_current_cpu &&
97         dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
98     {
99         if (addr.Offset)
100         {
101             addr.Offset -= dbghelp_current_cpu->word_size;
102             /* make sure stack pointer is within the established range of the stack.  It could have
103                been clobbered by whatever caused the original exception. */
104             if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
105                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
106 
107             else
108                 mmd->StartOfMemoryRange = addr.Offset;
109         }
110         else
111             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
112         mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
113     }
114 }
115 
116 /******************************************************************
117  *		fetch_thread_info
118  *
119  * fetches some information about thread of id 'tid'
120  */
121 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
122                               const MINIDUMP_EXCEPTION_INFORMATION* except,
123                               MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
124 {
125     DWORD                       tid = dc->threads[thd_idx].tid;
126     HANDLE                      hThread;
127     THREAD_BASIC_INFORMATION    tbi;
128 
129     memset(ctx, 0, sizeof(*ctx));
130 
131     mdThd->ThreadId = tid;
132     mdThd->SuspendCount = 0;
133     mdThd->Teb = 0;
134     mdThd->Stack.StartOfMemoryRange = 0;
135     mdThd->Stack.Memory.DataSize = 0;
136     mdThd->Stack.Memory.Rva = 0;
137     mdThd->ThreadContext.DataSize = 0;
138     mdThd->ThreadContext.Rva = 0;
139     mdThd->PriorityClass = dc->threads[thd_idx].prio_class;
140     mdThd->Priority = dc->threads[thd_idx].curr_prio;
141 
142     if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
143     {
144         FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
145         return FALSE;
146     }
147 
148     if (NtQueryInformationThread(hThread, ThreadBasicInformation,
149                                  &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
150     {
151         mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
152         if (tbi.ExitStatus == STILL_ACTIVE)
153         {
154             if (tid != GetCurrentThreadId() &&
155                 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
156             {
157                 ctx->ContextFlags = CONTEXT_FULL;
158                 if (!GetThreadContext(hThread, ctx))
159                     memset(ctx, 0, sizeof(*ctx));
160 
161                 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
162                 ResumeThread(hThread);
163             }
164             else if (tid == GetCurrentThreadId() && except)
165             {
166                 CONTEXT lctx, *pctx;
167                 mdThd->SuspendCount = 1;
168                 if (except->ClientPointers)
169                 {
170                     EXCEPTION_POINTERS      ep;
171 
172                     ReadProcessMemory(dc->process->handle, except->ExceptionPointers,
173                                       &ep, sizeof(ep), NULL);
174                     ReadProcessMemory(dc->process->handle, ep.ContextRecord,
175                                       &lctx, sizeof(lctx), NULL);
176                     pctx = &lctx;
177                 }
178                 else pctx = except->ExceptionPointers->ContextRecord;
179 
180                 *ctx = *pctx;
181                 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
182             }
183             else mdThd->SuspendCount = 0;
184         }
185     }
186     CloseHandle(hThread);
187     return TRUE;
188 }
189 
190 /******************************************************************
191  *		add_module
192  *
193  * Add a module to a dump context
194  */
195 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
196                        DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
197                        BOOL is_elf)
198 {
199     if (!dc->modules)
200     {
201         dc->alloc_modules = 32;
202         dc->modules = HeapAlloc(GetProcessHeap(), 0,
203                                 dc->alloc_modules * sizeof(*dc->modules));
204     }
205     else if(dc->num_modules >= dc->alloc_modules)
206     {
207         dc->alloc_modules *= 2;
208         dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
209                                   dc->alloc_modules * sizeof(*dc->modules));
210     }
211     if (!dc->modules)
212     {
213         dc->alloc_modules = dc->num_modules = 0;
214         return FALSE;
215     }
216     if (is_elf ||
217         !GetModuleFileNameExW(dc->process->handle, (HMODULE)(DWORD_PTR)base,
218                               dc->modules[dc->num_modules].name,
219                               ARRAY_SIZE(dc->modules[dc->num_modules].name)))
220         lstrcpynW(dc->modules[dc->num_modules].name, name,
221                   ARRAY_SIZE(dc->modules[dc->num_modules].name));
222     dc->modules[dc->num_modules].base = base;
223     dc->modules[dc->num_modules].size = size;
224     dc->modules[dc->num_modules].timestamp = timestamp;
225     dc->modules[dc->num_modules].checksum = checksum;
226     dc->modules[dc->num_modules].is_elf = is_elf;
227     dc->num_modules++;
228 
229     return TRUE;
230 }
231 
232 /******************************************************************
233  *		fetch_pe_module_info_cb
234  *
235  * Callback for accumulating in dump_context a PE modules set
236  */
237 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
238                                            PVOID user)
239 {
240     struct dump_context*        dc = user;
241     IMAGE_NT_HEADERS            nth;
242 
243     if (!validate_addr64(base)) return FALSE;
244 
245     if (pe_load_nt_header(dc->process->handle, base, &nth))
246         add_module(user, name, base, size,
247                    nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
248                    FALSE);
249     return TRUE;
250 }
251 
252 /******************************************************************
253  *		fetch_elf_module_info_cb
254  *
255  * Callback for accumulating in dump_context an host modules set
256  */
257 static BOOL fetch_host_module_info_cb(const WCHAR* name, ULONG_PTR base,
258                                      void* user)
259 {
260     struct dump_context*        dc = user;
261     DWORD_PTR                   rbase;
262     DWORD                       size, checksum;
263 
264     /* FIXME: there's no relevant timestamp on ELF modules */
265     if (!dc->process->loader->fetch_file_info(dc->process, name, base, &rbase, &size, &checksum))
266         size = checksum = 0;
267     add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
268     return TRUE;
269 }
270 
271 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
272 {
273     if (!dc->mem64)
274     {
275         dc->alloc_mem64 = 32;
276         dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
277     }
278     else if (dc->num_mem64 >= dc->alloc_mem64)
279     {
280         dc->alloc_mem64 *= 2;
281         dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
282                                 dc->alloc_mem64 * sizeof(*dc->mem64));
283     }
284     if (dc->mem64)
285     {
286         dc->mem64[dc->num_mem64].base = base;
287         dc->mem64[dc->num_mem64].size = size;
288         dc->num_mem64++;
289     }
290     else dc->num_mem64 = dc->alloc_mem64 = 0;
291 }
292 
293 static void fetch_memory64_info(struct dump_context* dc)
294 {
295     ULONG_PTR                   addr;
296     MEMORY_BASIC_INFORMATION    mbi;
297 
298     addr = 0;
299     while (VirtualQueryEx(dc->process->handle, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
300     {
301         /* Memory regions with state MEM_COMMIT will be added to the dump */
302         if (mbi.State == MEM_COMMIT)
303         {
304             minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
305         }
306 
307         if ((addr + mbi.RegionSize) < addr)
308             break;
309 
310         addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
311     }
312 }
313 
314 static void fetch_modules_info(struct dump_context* dc)
315 {
316     EnumerateLoadedModulesW64(dc->process->handle, fetch_pe_module_info_cb, dc);
317     /* Since we include ELF modules in a separate stream from the regular PE ones,
318      * we can always include those ELF modules (they don't eat lots of space)
319      * And it's always a good idea to have a trace of the loaded ELF modules for
320      * a given application in a post mortem debugging condition.
321      */
322     dc->process->loader->enum_modules(dc->process, fetch_host_module_info_cb, dc);
323 }
324 
325 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
326 {
327     DWORD       handle;
328     DWORD       sz;
329     static const WCHAR backslashW[] = {'\\', '\0'};
330 
331     memset(ffi, 0, sizeof(*ffi));
332     if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
333     {
334         void*   info = HeapAlloc(GetProcessHeap(), 0, sz);
335         if (info && GetFileVersionInfoW(filename, handle, sz, info))
336         {
337             VS_FIXEDFILEINFO*   ptr;
338             UINT    len;
339 
340             if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
341                 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
342         }
343         HeapFree(GetProcessHeap(), 0, info);
344     }
345 }
346 
347 /******************************************************************
348  *		minidump_add_memory_block
349  *
350  * Add a memory block to be dumped in a minidump
351  * If rva is non 0, it's the rva in the minidump where has to be stored
352  * also the rva of the memory block when written (this allows us to reference
353  * a memory block from outside the list of memory blocks).
354  */
355 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
356 {
357     if (!dc->mem)
358     {
359         dc->alloc_mem = 32;
360         dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
361     }
362     else if (dc->num_mem >= dc->alloc_mem)
363     {
364         dc->alloc_mem *= 2;
365         dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
366                               dc->alloc_mem * sizeof(*dc->mem));
367     }
368     if (dc->mem)
369     {
370         dc->mem[dc->num_mem].base = base;
371         dc->mem[dc->num_mem].size = size;
372         dc->mem[dc->num_mem].rva  = rva;
373         dc->num_mem++;
374     }
375     else dc->num_mem = dc->alloc_mem = 0;
376 }
377 
378 /******************************************************************
379  *		writeat
380  *
381  * Writes a chunk of data at a given position in the minidump
382  */
383 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
384 {
385     DWORD       written;
386 
387     SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
388     WriteFile(dc->hFile, data, size, &written, NULL);
389 }
390 
391 /******************************************************************
392  *		append
393  *
394  * writes a new chunk of data to the minidump, increasing the current
395  * rva in dc
396  */
397 static void append(struct dump_context* dc, const void* data, unsigned size)
398 {
399     writeat(dc, dc->rva, data, size);
400     dc->rva += size;
401 }
402 
403 /******************************************************************
404  *		dump_exception_info
405  *
406  * Write in File the exception information from pcs
407  */
408 static  unsigned        dump_exception_info(struct dump_context* dc,
409                                             const MINIDUMP_EXCEPTION_INFORMATION* except)
410 {
411     MINIDUMP_EXCEPTION_STREAM   mdExcpt;
412     EXCEPTION_RECORD            rec, *prec;
413     CONTEXT                     ctx, *pctx;
414     DWORD                       i;
415 
416     mdExcpt.ThreadId = except->ThreadId;
417     mdExcpt.__alignment = 0;
418     if (except->ClientPointers)
419     {
420         EXCEPTION_POINTERS      ep;
421 
422         ReadProcessMemory(dc->process->handle,
423                           except->ExceptionPointers, &ep, sizeof(ep), NULL);
424         ReadProcessMemory(dc->process->handle,
425                           ep.ExceptionRecord, &rec, sizeof(rec), NULL);
426         ReadProcessMemory(dc->process->handle,
427                           ep.ContextRecord, &ctx, sizeof(ctx), NULL);
428         prec = &rec;
429         pctx = &ctx;
430     }
431     else
432     {
433         prec = except->ExceptionPointers->ExceptionRecord;
434         pctx = except->ExceptionPointers->ContextRecord;
435     }
436     mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
437     mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
438     mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
439     mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
440     mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
441     mdExcpt.ExceptionRecord.__unusedAlignment = 0;
442     for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
443         mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
444     mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
445     mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
446 
447     append(dc, &mdExcpt, sizeof(mdExcpt));
448     append(dc, pctx, sizeof(*pctx));
449     return sizeof(mdExcpt);
450 }
451 
452 /******************************************************************
453  *		dump_modules
454  *
455  * Write in File the modules from pcs
456  */
457 static  unsigned        dump_modules(struct dump_context* dc, BOOL dump_elf)
458 {
459     MINIDUMP_MODULE             mdModule;
460     MINIDUMP_MODULE_LIST        mdModuleList;
461     char                        tmp[1024];
462     MINIDUMP_STRING*            ms = (MINIDUMP_STRING*)tmp;
463     ULONG                       i, nmod;
464     RVA                         rva_base;
465     DWORD                       flags_out;
466     unsigned                    sz;
467 
468     for (i = nmod = 0; i < dc->num_modules; i++)
469     {
470         if ((dc->modules[i].is_elf && dump_elf) ||
471             (!dc->modules[i].is_elf && !dump_elf))
472             nmod++;
473     }
474 
475     mdModuleList.NumberOfModules = 0;
476     /* reserve space for mdModuleList
477      * FIXME: since we don't support 0 length arrays, we cannot use the
478      * size of mdModuleList
479      * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
480      */
481 
482     /* the stream size is just the size of the module index.  It does not include the data for the
483        names of each module.  *Technically* the names are supposed to go into the common string table
484        in the minidump file.  Since each string is referenced by RVA they can all safely be located
485        anywhere between streams in the file, so the end of this stream is sufficient. */
486     rva_base = dc->rva;
487     dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
488     for (i = 0; i < dc->num_modules; i++)
489     {
490         if ((dc->modules[i].is_elf && !dump_elf) ||
491             (!dc->modules[i].is_elf && dump_elf))
492             continue;
493 
494         flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
495         if (dc->type & MiniDumpWithDataSegs)
496             flags_out |= ModuleWriteDataSeg;
497         if (dc->type & MiniDumpWithProcessThreadData)
498             flags_out |= ModuleWriteTlsData;
499         if (dc->type & MiniDumpWithCodeSegs)
500             flags_out |= ModuleWriteCodeSegs;
501         ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
502         if (sizeof(ULONG) + ms->Length > sizeof(tmp))
503             FIXME("Buffer overflow!!!\n");
504         lstrcpyW(ms->Buffer, dc->modules[i].name);
505 
506         if (dc->cb)
507         {
508             MINIDUMP_CALLBACK_INPUT     cbin;
509             MINIDUMP_CALLBACK_OUTPUT    cbout;
510 
511             cbin.ProcessId = dc->pid;
512             cbin.ProcessHandle = dc->process->handle;
513             cbin.CallbackType = ModuleCallback;
514 
515             cbin.u.Module.FullPath = ms->Buffer;
516             cbin.u.Module.BaseOfImage = dc->modules[i].base;
517             cbin.u.Module.SizeOfImage = dc->modules[i].size;
518             cbin.u.Module.CheckSum = dc->modules[i].checksum;
519             cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
520             memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
521             cbin.u.Module.CvRecord = NULL;
522             cbin.u.Module.SizeOfCvRecord = 0;
523             cbin.u.Module.MiscRecord = NULL;
524             cbin.u.Module.SizeOfMiscRecord = 0;
525 
526             cbout.u.ModuleWriteFlags = flags_out;
527             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
528                 continue;
529             flags_out &= cbout.u.ModuleWriteFlags;
530         }
531         if (flags_out & ModuleWriteModule)
532         {
533             /* fetch CPU dependent module info (like UNWIND_INFO) */
534             dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
535 
536             mdModule.BaseOfImage = dc->modules[i].base;
537             mdModule.SizeOfImage = dc->modules[i].size;
538             mdModule.CheckSum = dc->modules[i].checksum;
539             mdModule.TimeDateStamp = dc->modules[i].timestamp;
540             mdModule.ModuleNameRva = dc->rva;
541             ms->Length -= sizeof(WCHAR);
542             append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
543             fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
544             mdModule.CvRecord.DataSize = 0; /* FIXME */
545             mdModule.CvRecord.Rva = 0; /* FIXME */
546             mdModule.MiscRecord.DataSize = 0; /* FIXME */
547             mdModule.MiscRecord.Rva = 0; /* FIXME */
548             mdModule.Reserved0 = 0; /* FIXME */
549             mdModule.Reserved1 = 0; /* FIXME */
550             writeat(dc,
551                     rva_base + sizeof(mdModuleList.NumberOfModules) +
552                         mdModuleList.NumberOfModules++ * sizeof(mdModule),
553                     &mdModule, sizeof(mdModule));
554         }
555     }
556     writeat(dc, rva_base, &mdModuleList.NumberOfModules,
557             sizeof(mdModuleList.NumberOfModules));
558 
559     return sz;
560 }
561 
562 #ifdef __i386__
563 #ifndef __REACTOS__
564 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
565 __ASM_GLOBAL_FUNC( do_x86cpuid,
566                    "pushl %esi\n\t"
567                    "pushl %ebx\n\t"
568                    "movl 12(%esp),%eax\n\t"
569                    "movl 16(%esp),%esi\n\t"
570                    "cpuid\n\t"
571                    "movl %eax,(%esi)\n\t"
572                    "movl %ebx,4(%esi)\n\t"
573                    "movl %ecx,8(%esi)\n\t"
574                    "movl %edx,12(%esi)\n\t"
575                    "popl %ebx\n\t"
576                    "popl %esi\n\t"
577                    "ret" )
578 extern int have_x86cpuid(void);
579 __ASM_GLOBAL_FUNC( have_x86cpuid,
580                    "pushfl\n\t"
581                    "pushfl\n\t"
582                    "movl (%esp),%ecx\n\t"
583                    "xorl $0x00200000,(%esp)\n\t"
584                    "popfl\n\t"
585                    "pushfl\n\t"
586                    "popl %eax\n\t"
587                    "popfl\n\t"
588                    "xorl %ecx,%eax\n\t"
589                    "andl $0x00200000,%eax\n\t"
590                    "ret" )
591 #else
592 static void do_x86cpuid(unsigned int ax, unsigned int *p)
593 {
594     __cpuid((int*)p, ax);
595 }
596 
597 static int have_x86cpuid(void)
598 {
599     ULONG CallerFlags, Flags;
600 
601     /* Read the original flags, and add the CPUID bit */
602     CallerFlags = __readeflags() ^ 0x200000;
603     __writeeflags(CallerFlags);
604 
605     /* Read our flags now */
606     Flags = __readeflags();
607 
608     /* Check if the bit stuck */
609     return (((CallerFlags ^ Flags) >> 21) & 1) ^ 1;
610 }
611 #endif
612 #else
613 static void do_x86cpuid(unsigned int ax, unsigned int *p)
614 {
615 }
616 
617 static int have_x86cpuid(void)
618 {
619     return 0;
620 }
621 #endif
622 
623 /******************************************************************
624  *		dump_system_info
625  *
626  * Dumps into File the information about the system
627  */
628 static  unsigned        dump_system_info(struct dump_context* dc)
629 {
630     MINIDUMP_SYSTEM_INFO        mdSysInfo;
631     SYSTEM_INFO                 sysInfo;
632     OSVERSIONINFOW              osInfo;
633     DWORD                       written;
634     ULONG                       slen;
635     DWORD                       wine_extra = 0;
636 
637     const char *(CDECL *wine_get_build_id)(void);
638     void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
639     const char* build_id = NULL;
640     const char* sys_name = NULL;
641     const char* release_name = NULL;
642 
643     GetSystemInfo(&sysInfo);
644     osInfo.dwOSVersionInfoSize = sizeof(osInfo);
645     GetVersionExW(&osInfo);
646 
647     wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
648     wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
649     if (wine_get_build_id && wine_get_host_version)
650     {
651         /* cheat minidump system information by adding specific wine information */
652         wine_extra = 4 + 4 * sizeof(slen);
653         build_id = wine_get_build_id();
654         wine_get_host_version(&sys_name, &release_name);
655         wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
656     }
657 
658     mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
659     mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
660     mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
661     mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
662     mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
663     mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
664     mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
665     mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
666     mdSysInfo.PlatformId = osInfo.dwPlatformId;
667 
668     mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
669     mdSysInfo.u1.Reserved1 = 0;
670     mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
671 
672     if (have_x86cpuid())
673     {
674         unsigned        regs0[4], regs1[4];
675 
676         do_x86cpuid(0, regs0);
677         mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
678         mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
679         mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
680         do_x86cpuid(1, regs1);
681         mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
682         mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
683         mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
684         if (regs0[1] == 0x68747541 /* "Auth" */ &&
685             regs0[3] == 0x69746e65 /* "enti" */ &&
686             regs0[2] == 0x444d4163 /* "cAMD" */)
687         {
688             do_x86cpuid(0x80000000, regs1);  /* get vendor cpuid level */
689             if (regs1[0] >= 0x80000001)
690             {
691                 do_x86cpuid(0x80000001, regs1);  /* get vendor features */
692                 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
693             }
694         }
695     }
696     else
697     {
698         unsigned        i;
699         ULONG64         one = 1;
700 
701         mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
702         mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
703 
704         for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
705             if (IsProcessorFeaturePresent(i))
706                 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
707     }
708     append(dc, &mdSysInfo, sizeof(mdSysInfo));
709 
710     /* write Wine specific system information just behind the structure, and before any string */
711     if (wine_extra)
712     {
713         static const char code[] = {'W','I','N','E'};
714 
715         WriteFile(dc->hFile, code, 4, &written, NULL);
716         /* number of sub-info, so that we can extend structure if needed */
717         slen = 3;
718         WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
719         /* we store offsets from just after the WINE marker */
720         slen = 4 * sizeof(DWORD);
721         WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
722         slen += strlen(build_id) + 1;
723         WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
724         slen += strlen(sys_name) + 1;
725         WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
726         WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
727         WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
728         WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
729         dc->rva += wine_extra;
730     }
731 
732     /* write the service pack version string after this stream.  It is referenced within the
733        stream by its RVA in the file. */
734     slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
735     WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
736     WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
737     dc->rva += sizeof(ULONG) + slen;
738 
739     return sizeof(mdSysInfo);
740 }
741 
742 /******************************************************************
743  *		dump_threads
744  *
745  * Dumps into File the information about running threads
746  */
747 static  unsigned        dump_threads(struct dump_context* dc,
748                                      const MINIDUMP_EXCEPTION_INFORMATION* except)
749 {
750     MINIDUMP_THREAD             mdThd;
751     MINIDUMP_THREAD_LIST        mdThdList;
752     unsigned                    i, sz;
753     RVA                         rva_base;
754     DWORD                       flags_out;
755     CONTEXT                     ctx;
756 
757     mdThdList.NumberOfThreads = 0;
758 
759     rva_base = dc->rva;
760     dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
761 
762     for (i = 0; i < dc->num_threads; i++)
763     {
764         fetch_thread_info(dc, i, except, &mdThd, &ctx);
765 
766         flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
767             ThreadWriteInstructionWindow;
768         if (dc->type & MiniDumpWithProcessThreadData)
769             flags_out |= ThreadWriteThreadData;
770         if (dc->type & MiniDumpWithThreadInfo)
771             flags_out |= ThreadWriteThreadInfo;
772 
773         if (dc->cb)
774         {
775             MINIDUMP_CALLBACK_INPUT     cbin;
776             MINIDUMP_CALLBACK_OUTPUT    cbout;
777 
778             cbin.ProcessId = dc->pid;
779             cbin.ProcessHandle = dc->process->handle;
780             cbin.CallbackType = ThreadCallback;
781             cbin.u.Thread.ThreadId = dc->threads[i].tid;
782             cbin.u.Thread.ThreadHandle = 0; /* FIXME */
783             cbin.u.Thread.Context = ctx;
784             cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
785             cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
786             cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
787                 mdThd.Stack.Memory.DataSize;
788 
789             cbout.u.ThreadWriteFlags = flags_out;
790             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
791                 continue;
792             flags_out &= cbout.u.ThreadWriteFlags;
793         }
794         if (flags_out & ThreadWriteThread)
795         {
796             if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
797             {
798                 mdThd.ThreadContext.Rva = dc->rva;
799                 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
800                 append(dc, &ctx, sizeof(CONTEXT));
801             }
802             if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
803             {
804                 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
805                                           mdThd.Stack.Memory.DataSize,
806                                           rva_base + sizeof(mdThdList.NumberOfThreads) +
807                                           mdThdList.NumberOfThreads * sizeof(mdThd) +
808                                           FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
809             }
810             writeat(dc,
811                     rva_base + sizeof(mdThdList.NumberOfThreads) +
812                         mdThdList.NumberOfThreads * sizeof(mdThd),
813                     &mdThd, sizeof(mdThd));
814             mdThdList.NumberOfThreads++;
815         }
816         /* fetch CPU dependent thread info (like 256 bytes around program counter */
817         dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
818     }
819     writeat(dc, rva_base,
820             &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
821 
822     return sz;
823 }
824 
825 /******************************************************************
826  *		dump_memory_info
827  *
828  * dumps information about the memory of the process (stack of the threads)
829  */
830 static unsigned         dump_memory_info(struct dump_context* dc)
831 {
832     MINIDUMP_MEMORY_LIST        mdMemList;
833     MINIDUMP_MEMORY_DESCRIPTOR  mdMem;
834     DWORD                       written;
835     unsigned                    i, pos, len, sz;
836     RVA                         rva_base;
837     char                        tmp[1024];
838 
839     mdMemList.NumberOfMemoryRanges = dc->num_mem;
840     append(dc, &mdMemList.NumberOfMemoryRanges,
841            sizeof(mdMemList.NumberOfMemoryRanges));
842     rva_base = dc->rva;
843     sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
844     dc->rva += sz;
845     sz += sizeof(mdMemList.NumberOfMemoryRanges);
846 
847     for (i = 0; i < dc->num_mem; i++)
848     {
849         mdMem.StartOfMemoryRange = dc->mem[i].base;
850         mdMem.Memory.Rva = dc->rva;
851         mdMem.Memory.DataSize = dc->mem[i].size;
852         SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
853         for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
854         {
855             len = min(dc->mem[i].size - pos, sizeof(tmp));
856             if (read_process_memory(dc->process, dc->mem[i].base + pos, tmp, len))
857                 WriteFile(dc->hFile, tmp, len, &written, NULL);
858         }
859         dc->rva += mdMem.Memory.DataSize;
860         writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
861         if (dc->mem[i].rva)
862         {
863             writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
864         }
865     }
866 
867     return sz;
868 }
869 
870 /******************************************************************
871  *		dump_memory64_info
872  *
873  * dumps information about the memory of the process (virtual memory)
874  */
875 static unsigned         dump_memory64_info(struct dump_context* dc)
876 {
877     MINIDUMP_MEMORY64_LIST          mdMem64List;
878     MINIDUMP_MEMORY_DESCRIPTOR64    mdMem64;
879     DWORD                           written;
880     unsigned                        i, len, sz;
881     RVA                             rva_base;
882     char                            tmp[1024];
883     ULONG64                         pos;
884     LARGE_INTEGER                   filepos;
885 
886     sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
887             sizeof(mdMem64List.BaseRva) +
888             dc->num_mem64 * sizeof(mdMem64);
889 
890     mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
891     mdMem64List.BaseRva = dc->rva + sz;
892 
893     append(dc, &mdMem64List.NumberOfMemoryRanges,
894            sizeof(mdMem64List.NumberOfMemoryRanges));
895     append(dc, &mdMem64List.BaseRva,
896            sizeof(mdMem64List.BaseRva));
897 
898     rva_base = dc->rva;
899     dc->rva += dc->num_mem64 * sizeof(mdMem64);
900 
901     /* dc->rva is not updated past this point. The end of the dump
902      * is just the full memory data. */
903     filepos.QuadPart = dc->rva;
904     for (i = 0; i < dc->num_mem64; i++)
905     {
906         mdMem64.StartOfMemoryRange = dc->mem64[i].base;
907         mdMem64.DataSize = dc->mem64[i].size;
908         SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
909         for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
910         {
911             len = min(dc->mem64[i].size - pos, sizeof(tmp));
912             if (read_process_memory(dc->process, dc->mem64[i].base + pos, tmp, len))
913                 WriteFile(dc->hFile, tmp, len, &written, NULL);
914         }
915         filepos.QuadPart += mdMem64.DataSize;
916         writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
917     }
918 
919     return sz;
920 }
921 
922 static unsigned         dump_misc_info(struct dump_context* dc)
923 {
924     MINIDUMP_MISC_INFO  mmi;
925 
926     mmi.SizeOfInfo = sizeof(mmi);
927     mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
928     mmi.ProcessId = dc->pid;
929     /* FIXME: create/user/kernel time */
930     mmi.ProcessCreateTime = 0;
931     mmi.ProcessKernelTime = 0;
932     mmi.ProcessUserTime = 0;
933 
934     append(dc, &mmi, sizeof(mmi));
935     return sizeof(mmi);
936 }
937 
938 /******************************************************************
939  *		MiniDumpWriteDump (DEBUGHLP.@)
940  *
941  */
942 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
943                               MINIDUMP_TYPE DumpType,
944                               PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
945                               PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
946                               PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
947 {
948     static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
949     MINIDUMP_HEADER     mdHead;
950     MINIDUMP_DIRECTORY  mdDir;
951     DWORD               i, nStreams, idx_stream;
952     struct dump_context dc;
953     BOOL                sym_initialized = FALSE;
954 
955     if (!(dc.process = process_find_by_handle(hProcess)))
956     {
957         if (!(sym_initialized = SymInitializeW(hProcess, NULL, TRUE)))
958         {
959             WARN("failed to initialize process\n");
960             return FALSE;
961         }
962         dc.process = process_find_by_handle(hProcess);
963     }
964 
965     dc.hFile = hFile;
966     dc.pid = pid;
967     dc.modules = NULL;
968     dc.num_modules = 0;
969     dc.alloc_modules = 0;
970     dc.threads = NULL;
971     dc.num_threads = 0;
972     dc.cb = CallbackParam;
973     dc.type = DumpType;
974     dc.mem = NULL;
975     dc.num_mem = 0;
976     dc.alloc_mem = 0;
977     dc.mem64 = NULL;
978     dc.num_mem64 = 0;
979     dc.alloc_mem64 = 0;
980     dc.rva = 0;
981 
982     if (!fetch_process_info(&dc)) return FALSE;
983     fetch_modules_info(&dc);
984 
985     /* 1) init */
986     nStreams = 6 + (ExceptionParam ? 1 : 0) +
987         (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
988 
989     /* pad the directory size to a multiple of 4 for alignment purposes */
990     nStreams = (nStreams + 3) & ~3;
991 
992     if (DumpType & MiniDumpWithDataSegs)
993         FIXME("NIY MiniDumpWithDataSegs\n");
994     if (DumpType & MiniDumpWithHandleData)
995         FIXME("NIY MiniDumpWithHandleData\n");
996     if (DumpType & MiniDumpFilterMemory)
997         FIXME("NIY MiniDumpFilterMemory\n");
998     if (DumpType & MiniDumpScanMemory)
999         FIXME("NIY MiniDumpScanMemory\n");
1000 
1001     /* 2) write header */
1002     mdHead.Signature = MINIDUMP_SIGNATURE;
1003     mdHead.Version = MINIDUMP_VERSION;  /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
1004     mdHead.NumberOfStreams = nStreams;
1005     mdHead.CheckSum = 0;                /* native sets a 0 checksum in its files */
1006     mdHead.StreamDirectoryRva = sizeof(mdHead);
1007     mdHead.u.TimeDateStamp = time(NULL);
1008     mdHead.Flags = DumpType;
1009     append(&dc, &mdHead, sizeof(mdHead));
1010 
1011     /* 3) write stream directories */
1012     dc.rva += nStreams * sizeof(mdDir);
1013     idx_stream = 0;
1014 
1015     /* 3.1) write data stream directories */
1016 
1017     /* must be first in minidump */
1018     mdDir.StreamType = SystemInfoStream;
1019     mdDir.Location.Rva = dc.rva;
1020     mdDir.Location.DataSize = dump_system_info(&dc);
1021     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1022             &mdDir, sizeof(mdDir));
1023 
1024     mdDir.StreamType = ThreadListStream;
1025     mdDir.Location.Rva = dc.rva;
1026     mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
1027     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1028             &mdDir, sizeof(mdDir));
1029 
1030     mdDir.StreamType = ModuleListStream;
1031     mdDir.Location.Rva = dc.rva;
1032     mdDir.Location.DataSize = dump_modules(&dc, FALSE);
1033     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1034             &mdDir, sizeof(mdDir));
1035 
1036     mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1037     mdDir.Location.Rva = dc.rva;
1038     mdDir.Location.DataSize = dump_modules(&dc, TRUE);
1039     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1040             &mdDir, sizeof(mdDir));
1041 
1042 
1043     if (!(DumpType & MiniDumpWithFullMemory))
1044     {
1045         mdDir.StreamType = MemoryListStream;
1046         mdDir.Location.Rva = dc.rva;
1047         mdDir.Location.DataSize = dump_memory_info(&dc);
1048         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1049                 &mdDir, sizeof(mdDir));
1050     }
1051 
1052     mdDir.StreamType = MiscInfoStream;
1053     mdDir.Location.Rva = dc.rva;
1054     mdDir.Location.DataSize = dump_misc_info(&dc);
1055     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1056             &mdDir, sizeof(mdDir));
1057 
1058     /* 3.2) write exception information (if any) */
1059     if (ExceptionParam)
1060     {
1061         mdDir.StreamType = ExceptionStream;
1062         mdDir.Location.Rva = dc.rva;
1063         mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
1064         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1065                 &mdDir, sizeof(mdDir));
1066     }
1067 
1068     /* 3.3) write user defined streams (if any) */
1069     if (UserStreamParam)
1070     {
1071         for (i = 0; i < UserStreamParam->UserStreamCount; i++)
1072         {
1073             mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
1074             mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
1075             mdDir.Location.Rva = dc.rva;
1076             writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1077                     &mdDir, sizeof(mdDir));
1078             append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
1079                    UserStreamParam->UserStreamArray[i].BufferSize);
1080         }
1081     }
1082 
1083     /* 3.4) write full memory (if requested) */
1084     if (DumpType & MiniDumpWithFullMemory)
1085     {
1086         fetch_memory64_info(&dc);
1087 
1088         mdDir.StreamType = Memory64ListStream;
1089         mdDir.Location.Rva = dc.rva;
1090         mdDir.Location.DataSize = dump_memory64_info(&dc);
1091         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1092                 &mdDir, sizeof(mdDir));
1093     }
1094 
1095     /* fill the remaining directory entries with 0's (unused stream types) */
1096     /* NOTE: this should always come last in the dump! */
1097     for (i = idx_stream; i < nStreams; i++)
1098         writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1099 
1100     if (sym_initialized)
1101         SymCleanup(hProcess);
1102 
1103     HeapFree(GetProcessHeap(), 0, dc.mem);
1104     HeapFree(GetProcessHeap(), 0, dc.mem64);
1105     HeapFree(GetProcessHeap(), 0, dc.modules);
1106     HeapFree(GetProcessHeap(), 0, dc.threads);
1107 
1108     return TRUE;
1109 }
1110 
1111 /******************************************************************
1112  *		MiniDumpReadDumpStream (DEBUGHLP.@)
1113  *
1114  *
1115  */
1116 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1117                                    PMINIDUMP_DIRECTORY* pdir,
1118                                    PVOID* stream, ULONG* size)
1119 {
1120     MINIDUMP_HEADER*    mdHead = base;
1121 
1122     if (mdHead->Signature == MINIDUMP_SIGNATURE)
1123     {
1124         MINIDUMP_DIRECTORY* dir;
1125         DWORD               i;
1126 
1127         dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1128         for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1129         {
1130             if (dir->StreamType == str_idx)
1131             {
1132                 if (pdir) *pdir = dir;
1133                 if (stream) *stream = (char*)base + dir->Location.Rva;
1134                 if (size) *size = dir->Location.DataSize;
1135                 return TRUE;
1136             }
1137         }
1138     }
1139     SetLastError(ERROR_INVALID_PARAMETER);
1140     return FALSE;
1141 }
1142