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