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