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