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