dbghelp: Assign to struct instead of using memcpy.
[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 (dc->spi->dwProcessID == dc->pid) return TRUE;
102             if (!dc->spi->dwOffset) break;
103             dc->spi = (SYSTEM_PROCESS_INFORMATION*)     
104                 ((char*)dc->spi + dc->spi->dwOffset);
105         }
106     }
107     HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
108     dc->pcs_buffer = NULL;
109     dc->spi = NULL;
110     return FALSE;
111 }
112
113 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
114                                const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
115 {
116     NT_TIB      tib;
117
118     if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
119     {
120 #ifdef __i386__
121         /* limiting the stack dumping to the size actually used */
122         if (ctx->Esp){
123
124             /* make sure ESP is within the established range of the stack.  It could have
125                been clobbered by whatever caused the original exception. */
126             if (ctx->Esp - 4 < (ULONG_PTR)tib.StackLimit || ctx->Esp - 4 > (ULONG_PTR)tib.StackBase)
127                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
128
129             else
130                 mmd->StartOfMemoryRange = (ctx->Esp - 4);
131         }
132
133         else
134             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
135
136 #elif defined(__powerpc__)
137         if (ctx->Iar){
138
139             /* make sure IAR is within the established range of the stack.  It could have
140                been clobbered by whatever caused the original exception. */
141             if (ctx->Iar - 4 < (ULONG_PTR)tib.StackLimit || ctx->Iar - 4 > (ULONG_PTR)tib.StackBase)
142                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
143
144             else
145                 mmd->StartOfMemoryRange = (ctx->Iar - 4);
146         }
147
148         else
149             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
150
151 #elif defined(__x86_64__)
152         if (ctx->Rsp){
153
154             /* make sure RSP is within the established range of the stack.  It could have
155                been clobbered by whatever caused the original exception. */
156             if (ctx->Rsp - 8 < (ULONG_PTR)tib.StackLimit || ctx->Rsp - 8 > (ULONG_PTR)tib.StackBase)
157                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
158
159             else
160                 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
161         }
162
163         else
164             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
165
166 #else
167 #error unsupported CPU
168 #endif
169         mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
170     }
171 }
172
173 /******************************************************************
174  *              fetch_thread_info
175  *
176  * fetches some information about thread of id 'tid'
177  */
178 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
179                               const MINIDUMP_EXCEPTION_INFORMATION* except,
180                               MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
181 {
182     DWORD                       tid = dc->spi->ti[thd_idx].dwThreadID;
183     HANDLE                      hThread;
184     THREAD_BASIC_INFORMATION    tbi;
185
186     memset(ctx, 0, sizeof(*ctx));
187
188     mdThd->ThreadId = dc->spi->ti[thd_idx].dwThreadID;
189     mdThd->SuspendCount = 0;
190     mdThd->Teb = 0;
191     mdThd->Stack.StartOfMemoryRange = 0;
192     mdThd->Stack.Memory.DataSize = 0;
193     mdThd->Stack.Memory.Rva = 0;
194     mdThd->ThreadContext.DataSize = 0;
195     mdThd->ThreadContext.Rva = 0;
196     mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
197     mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
198
199     if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
200     {
201         FIXME("Couldn't open thread %u (%u)\n",
202               dc->spi->ti[thd_idx].dwThreadID, 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         dc->modules = HeapAlloc(GetProcessHeap(), 0,
259                                 ++dc->num_modules * sizeof(*dc->modules));
260     else
261         dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
262                                   ++dc->num_modules * sizeof(*dc->modules));
263     if (!dc->modules) return FALSE;
264     if (is_elf ||
265         !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
266                               dc->modules[dc->num_modules - 1].name,
267                               sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR)))
268         lstrcpynW(dc->modules[dc->num_modules - 1].name, name,
269                   sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR));
270     dc->modules[dc->num_modules - 1].base = base;
271     dc->modules[dc->num_modules - 1].size = size;
272     dc->modules[dc->num_modules - 1].timestamp = timestamp;
273     dc->modules[dc->num_modules - 1].checksum = checksum;
274     dc->modules[dc->num_modules - 1].is_elf = is_elf;
275
276     return TRUE;
277 }
278
279 /******************************************************************
280  *              fetch_pe_module_info_cb
281  *
282  * Callback for accumulating in dump_context a PE modules set
283  */
284 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
285                                            PVOID user)
286 {
287     struct dump_context*        dc = (struct dump_context*)user;
288     IMAGE_NT_HEADERS            nth;
289
290     if (!validate_addr64(base)) return FALSE;
291
292     if (pe_load_nt_header(dc->hProcess, base, &nth))
293         add_module((struct dump_context*)user, name, base, size,
294                    nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
295                    FALSE);
296     return TRUE;
297 }
298
299 /******************************************************************
300  *              fetch_elf_module_info_cb
301  *
302  * Callback for accumulating in dump_context an ELF modules set
303  */
304 static BOOL fetch_elf_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 ELF modules */
311     /* NB: if we have a non-null base from the live-target use it (whenever
312      * the ELF module is relocatable or not). If we have a null base (ELF
313      * module isn't relocatable) then grab its base address from ELF file
314      */
315     if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
316         size = checksum = 0;
317     add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
318     return TRUE;
319 }
320
321 static void fetch_modules_info(struct dump_context* dc)
322 {
323     EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
324     /* Since we include ELF modules in a separate stream from the regular PE ones,
325      * we can always include those ELF modules (they don't eat lots of space)
326      * And it's always a good idea to have a trace of the loaded ELF modules for
327      * a given application in a post mortem debugging condition.
328      */
329     elf_enum_modules(dc->hProcess, fetch_elf_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         dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 
366                               ++dc->num_mem * sizeof(*dc->mem));
367     else
368         dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
369     if (dc->mem)
370     {
371         dc->mem[dc->num_mem - 1].base = base;
372         dc->mem[dc->num_mem - 1].size = size;
373         dc->mem[dc->num_mem - 1].rva  = rva;
374     }
375     else dc->num_mem = 0;
376 }
377
378 /******************************************************************
379  *              writeat
380  *
381  * Writes a chunk of data at a given position in the minidump
382  */
383 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
384 {
385     DWORD       written;
386
387     SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
388     WriteFile(dc->hFile, data, size, &written, NULL);
389 }
390
391 /******************************************************************
392  *              append
393  *
394  * writes a new chunk of data to the minidump, increasing the current
395  * rva in dc
396  */
397 static void append(struct dump_context* dc, void* data, unsigned size)
398 {
399     writeat(dc, dc->rva, data, size);
400     dc->rva += size;
401 }
402
403 /******************************************************************
404  *              dump_exception_info
405  *
406  * Write in File the exception information from pcs
407  */
408 static  unsigned        dump_exception_info(struct dump_context* dc,
409                                             const MINIDUMP_EXCEPTION_INFORMATION* except)
410 {
411     MINIDUMP_EXCEPTION_STREAM   mdExcpt;
412     EXCEPTION_RECORD            rec, *prec;
413     CONTEXT                     ctx, *pctx;
414     int                         i;
415
416     mdExcpt.ThreadId = except->ThreadId;
417     mdExcpt.__alignment = 0;
418     if (except->ClientPointers)
419     {
420         EXCEPTION_POINTERS      ep;
421
422         ReadProcessMemory(dc->hProcess, 
423                           except->ExceptionPointers, &ep, sizeof(ep), NULL);
424         ReadProcessMemory(dc->hProcess, 
425                           ep.ExceptionRecord, &rec, sizeof(rec), NULL);
426         ReadProcessMemory(dc->hProcess, 
427                           ep.ContextRecord, &ctx, sizeof(ctx), NULL);
428         prec = &rec;
429         pctx = &ctx;
430     }
431     else
432     {
433         prec = except->ExceptionPointers->ExceptionRecord;
434         pctx = except->ExceptionPointers->ContextRecord;
435     }
436     mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
437     mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
438     mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
439     mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
440     mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
441     mdExcpt.ExceptionRecord.__unusedAlignment = 0;
442     for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
443         mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
444     mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
445     mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
446
447     append(dc, &mdExcpt, sizeof(mdExcpt));
448     append(dc, pctx, sizeof(*pctx));
449     return sizeof(mdExcpt);
450 }
451
452 /******************************************************************
453  *              dump_modules
454  *
455  * Write in File the modules from pcs
456  */
457 static  unsigned        dump_modules(struct dump_context* dc, BOOL dump_elf)
458 {
459     MINIDUMP_MODULE             mdModule;
460     MINIDUMP_MODULE_LIST        mdModuleList;
461     char                        tmp[1024];
462     MINIDUMP_STRING*            ms = (MINIDUMP_STRING*)tmp;
463     ULONG                       i, nmod;
464     RVA                         rva_base;
465     DWORD                       flags_out;
466     unsigned                    sz;
467
468     for (i = nmod = 0; i < dc->num_modules; i++)
469     {
470         if ((dc->modules[i].is_elf && dump_elf) ||
471             (!dc->modules[i].is_elf && !dump_elf))
472             nmod++;
473     }
474
475     mdModuleList.NumberOfModules = 0;
476     /* reserve space for mdModuleList
477      * FIXME: since we don't support 0 length arrays, we cannot use the
478      * size of mdModuleList
479      * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
480      */
481
482     /* the stream size is just the size of the module index.  It does not include the data for the
483        names of each module.  *Technically* the names are supposed to go into the common string table
484        in the minidump file.  Since each string is referenced by RVA they can all safely be located
485        anywhere between streams in the file, so the end of this stream is sufficient. */
486     rva_base = dc->rva;
487     dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
488     for (i = 0; i < dc->num_modules; i++)
489     {
490         if ((dc->modules[i].is_elf && !dump_elf) ||
491             (!dc->modules[i].is_elf && dump_elf))
492             continue;
493
494         flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
495         if (dc->type & MiniDumpWithDataSegs)
496             flags_out |= ModuleWriteDataSeg;
497         if (dc->type & MiniDumpWithProcessThreadData)
498             flags_out |= ModuleWriteTlsData;
499         if (dc->type & MiniDumpWithCodeSegs)
500             flags_out |= ModuleWriteCodeSegs;
501         ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
502         if (sizeof(ULONG) + ms->Length > sizeof(tmp))
503             FIXME("Buffer overflow!!!\n");
504         lstrcpyW(ms->Buffer, dc->modules[i].name);
505
506         if (dc->cb)
507         {
508             MINIDUMP_CALLBACK_INPUT     cbin;
509             MINIDUMP_CALLBACK_OUTPUT    cbout;
510
511             cbin.ProcessId = dc->pid;
512             cbin.ProcessHandle = dc->hProcess;
513             cbin.CallbackType = ModuleCallback;
514
515             cbin.u.Module.FullPath = ms->Buffer;
516             cbin.u.Module.BaseOfImage = dc->modules[i].base;
517             cbin.u.Module.SizeOfImage = dc->modules[i].size;
518             cbin.u.Module.CheckSum = dc->modules[i].checksum;
519             cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
520             memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
521             cbin.u.Module.CvRecord = NULL;
522             cbin.u.Module.SizeOfCvRecord = 0;
523             cbin.u.Module.MiscRecord = NULL;
524             cbin.u.Module.SizeOfMiscRecord = 0;
525
526             cbout.u.ModuleWriteFlags = flags_out;
527             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
528                 continue;
529             flags_out &= cbout.u.ModuleWriteFlags;
530         }
531         if (flags_out & ModuleWriteModule)
532         {
533             mdModule.BaseOfImage = dc->modules[i].base;
534             mdModule.SizeOfImage = dc->modules[i].size;
535             mdModule.CheckSum = dc->modules[i].checksum;
536             mdModule.TimeDateStamp = dc->modules[i].timestamp;
537             mdModule.ModuleNameRva = dc->rva;
538             ms->Length -= sizeof(WCHAR);
539             append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
540             fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
541             mdModule.CvRecord.DataSize = 0; /* FIXME */
542             mdModule.CvRecord.Rva = 0; /* FIXME */
543             mdModule.MiscRecord.DataSize = 0; /* FIXME */
544             mdModule.MiscRecord.Rva = 0; /* FIXME */
545             mdModule.Reserved0 = 0; /* FIXME */
546             mdModule.Reserved1 = 0; /* FIXME */
547             writeat(dc,
548                     rva_base + sizeof(mdModuleList.NumberOfModules) + 
549                         mdModuleList.NumberOfModules++ * sizeof(mdModule), 
550                     &mdModule, sizeof(mdModule));
551         }
552     }
553     writeat(dc, rva_base, &mdModuleList.NumberOfModules, 
554             sizeof(mdModuleList.NumberOfModules));
555
556     return sz;
557 }
558
559 /******************************************************************
560  *              dump_system_info
561  *
562  * Dumps into File the information about the system
563  */
564 static  unsigned        dump_system_info(struct dump_context* dc)
565 {
566     MINIDUMP_SYSTEM_INFO        mdSysInfo;
567     SYSTEM_INFO                 sysInfo;
568     OSVERSIONINFOW              osInfo;
569     DWORD                       written;
570     ULONG                       slen;
571
572     GetSystemInfo(&sysInfo);
573     osInfo.dwOSVersionInfoSize = sizeof(osInfo);
574     GetVersionExW(&osInfo);
575
576     mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
577     mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
578     mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
579     mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
580     mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
581     mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
582     mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
583     mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
584     mdSysInfo.PlatformId = osInfo.dwPlatformId;
585
586     mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
587     mdSysInfo.u1.Reserved1 = 0;
588     mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
589
590     FIXME("fill in CPU vendorID and feature set\n");
591     memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
592
593     append(dc, &mdSysInfo, sizeof(mdSysInfo));
594
595     /* write the service pack version string after this stream.  It is referenced within the
596        stream by its RVA in the file. */
597     slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
598     WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
599     WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
600     dc->rva += sizeof(ULONG) + slen;
601
602     return sizeof(mdSysInfo);
603 }
604
605 /******************************************************************
606  *              dump_threads
607  *
608  * Dumps into File the information about running threads
609  */
610 static  unsigned        dump_threads(struct dump_context* dc,
611                                      const MINIDUMP_EXCEPTION_INFORMATION* except)
612 {
613     MINIDUMP_THREAD             mdThd;
614     MINIDUMP_THREAD_LIST        mdThdList;
615     unsigned                    i;
616     RVA                         rva_base;
617     DWORD                       flags_out;
618     CONTEXT                     ctx;
619
620     mdThdList.NumberOfThreads = 0;
621
622     rva_base = dc->rva;
623     dc->rva += sizeof(mdThdList.NumberOfThreads) +
624         dc->spi->dwThreadCount * sizeof(mdThd);
625
626     for (i = 0; i < dc->spi->dwThreadCount; i++)
627     {
628         fetch_thread_info(dc, i, except, &mdThd, &ctx);
629
630         flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
631             ThreadWriteInstructionWindow;
632         if (dc->type & MiniDumpWithProcessThreadData)
633             flags_out |= ThreadWriteThreadData;
634         if (dc->type & MiniDumpWithThreadInfo)
635             flags_out |= ThreadWriteThreadInfo;
636
637         if (dc->cb)
638         {
639             MINIDUMP_CALLBACK_INPUT     cbin;
640             MINIDUMP_CALLBACK_OUTPUT    cbout;
641
642             cbin.ProcessId = dc->pid;
643             cbin.ProcessHandle = dc->hProcess;
644             cbin.CallbackType = ThreadCallback;
645             cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
646             cbin.u.Thread.ThreadHandle = 0; /* FIXME */
647             cbin.u.Thread.Context = ctx;
648             cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
649             cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
650             cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
651                 mdThd.Stack.Memory.DataSize;
652
653             cbout.u.ThreadWriteFlags = flags_out;
654             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
655                 continue;
656             flags_out &= cbout.u.ThreadWriteFlags;
657         }
658         if (flags_out & ThreadWriteThread)
659         {
660             if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
661             {
662                 mdThd.ThreadContext.Rva = dc->rva;
663                 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
664                 append(dc, &ctx, sizeof(CONTEXT));
665             }
666             if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
667             {
668                 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
669                                  mdThd.Stack.Memory.DataSize,
670                                  rva_base + sizeof(mdThdList.NumberOfThreads) +
671                                      mdThdList.NumberOfThreads * sizeof(mdThd) +
672                                      FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
673             }
674             writeat(dc, 
675                     rva_base + sizeof(mdThdList.NumberOfThreads) +
676                         mdThdList.NumberOfThreads * sizeof(mdThd),
677                     &mdThd, sizeof(mdThd));
678             mdThdList.NumberOfThreads++;
679         }
680         if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
681         {
682             /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
683              *        - also crop values across module boundaries, 
684              *        - and don't make it i386 dependent 
685              */
686             /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
687         }
688     }
689     writeat(dc, rva_base,
690             &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
691
692     return dc->rva - rva_base;
693 }
694
695 /******************************************************************
696  *              dump_memory_info
697  *
698  * dumps information about the memory of the process (stack of the threads)
699  */
700 static unsigned         dump_memory_info(struct dump_context* dc)
701 {
702     MINIDUMP_MEMORY_LIST        mdMemList;
703     MINIDUMP_MEMORY_DESCRIPTOR  mdMem;
704     DWORD                       written;
705     unsigned                    i, pos, len, sz;
706     RVA                         rva_base;
707     char                        tmp[1024];
708
709     mdMemList.NumberOfMemoryRanges = dc->num_mem;
710     append(dc, &mdMemList.NumberOfMemoryRanges,
711            sizeof(mdMemList.NumberOfMemoryRanges));
712     rva_base = dc->rva;
713     sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
714     dc->rva += sz;
715     sz += sizeof(mdMemList.NumberOfMemoryRanges);
716
717     for (i = 0; i < dc->num_mem; i++)
718     {
719         mdMem.StartOfMemoryRange = dc->mem[i].base;
720         mdMem.Memory.Rva = dc->rva;
721         mdMem.Memory.DataSize = dc->mem[i].size;
722         SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
723         for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
724         {
725             len = min(dc->mem[i].size - pos, sizeof(tmp));
726             if (ReadProcessMemory(dc->hProcess, 
727                                   (void*)(dc->mem[i].base + pos),
728                                   tmp, len, NULL))
729                 WriteFile(dc->hFile, tmp, len, &written, NULL);
730         }
731         dc->rva += mdMem.Memory.DataSize;
732         writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
733         if (dc->mem[i].rva)
734         {
735             writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
736         }
737     }
738
739     return sz;
740 }
741
742 static unsigned         dump_misc_info(struct dump_context* dc)
743 {
744     MINIDUMP_MISC_INFO  mmi;
745
746     mmi.SizeOfInfo = sizeof(mmi);
747     mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
748     mmi.ProcessId = dc->pid;
749     /* FIXME: create/user/kernel time */
750     mmi.ProcessCreateTime = 0;
751     mmi.ProcessKernelTime = 0;
752     mmi.ProcessUserTime = 0;
753
754     append(dc, &mmi, sizeof(mmi));
755     return sizeof(mmi);
756 }
757
758 /******************************************************************
759  *              MiniDumpWriteDump (DEBUGHLP.@)
760  *
761  */
762 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
763                               MINIDUMP_TYPE DumpType,
764                               PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
765                               PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
766                               PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
767 {
768     static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
769     MINIDUMP_HEADER     mdHead;
770     MINIDUMP_DIRECTORY  mdDir;
771     DWORD               i, nStreams, idx_stream;
772     struct dump_context dc;
773
774     dc.hProcess = hProcess;
775     dc.hFile = hFile;
776     dc.pid = pid;
777     dc.modules = NULL;
778     dc.num_modules = 0;
779     dc.cb = CallbackParam;
780     dc.type = DumpType;
781     dc.mem = NULL;
782     dc.num_mem = 0;
783     dc.rva = 0;
784
785     if (!fetch_processes_info(&dc)) return FALSE;
786     fetch_modules_info(&dc);
787
788     /* 1) init */
789     nStreams = 6 + (ExceptionParam ? 1 : 0) +
790         (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
791
792     /* pad the directory size to a multiple of 4 for alignment purposes */
793     nStreams = (nStreams + 3) & ~3;
794
795     if (DumpType & MiniDumpWithDataSegs)
796         FIXME("NIY MiniDumpWithDataSegs\n");
797     if (DumpType & MiniDumpWithFullMemory)
798         FIXME("NIY MiniDumpWithFullMemory\n");
799     if (DumpType & MiniDumpWithHandleData)
800         FIXME("NIY MiniDumpWithHandleData\n");
801     if (DumpType & MiniDumpFilterMemory)
802         FIXME("NIY MiniDumpFilterMemory\n");
803     if (DumpType & MiniDumpScanMemory)
804         FIXME("NIY MiniDumpScanMemory\n");
805
806     /* 2) write header */
807     mdHead.Signature = MINIDUMP_SIGNATURE;
808     mdHead.Version = MINIDUMP_VERSION;  /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
809     mdHead.NumberOfStreams = nStreams;
810     mdHead.CheckSum = 0;                /* native sets a 0 checksum in its files */
811     mdHead.StreamDirectoryRva = sizeof(mdHead);
812     mdHead.u.TimeDateStamp = time(NULL);
813     mdHead.Flags = DumpType;
814     append(&dc, &mdHead, sizeof(mdHead));
815
816     /* 3) write stream directories */
817     dc.rva += nStreams * sizeof(mdDir);
818     idx_stream = 0;
819
820     /* 3.1) write data stream directories */
821
822     /* must be first in minidump */
823     mdDir.StreamType = SystemInfoStream;
824     mdDir.Location.Rva = dc.rva;
825     mdDir.Location.DataSize = dump_system_info(&dc);
826     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
827             &mdDir, sizeof(mdDir));
828
829     mdDir.StreamType = ThreadListStream;
830     mdDir.Location.Rva = dc.rva;
831     mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
832     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 
833             &mdDir, sizeof(mdDir));
834
835     mdDir.StreamType = ModuleListStream;
836     mdDir.Location.Rva = dc.rva;
837     mdDir.Location.DataSize = dump_modules(&dc, FALSE);
838     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
839             &mdDir, sizeof(mdDir));
840
841     mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
842     mdDir.Location.Rva = dc.rva;
843     mdDir.Location.DataSize = dump_modules(&dc, TRUE);
844     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
845             &mdDir, sizeof(mdDir));
846
847     mdDir.StreamType = MemoryListStream;
848     mdDir.Location.Rva = dc.rva;
849     mdDir.Location.DataSize = dump_memory_info(&dc);
850     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
851             &mdDir, sizeof(mdDir));
852
853     mdDir.StreamType = MiscInfoStream;
854     mdDir.Location.Rva = dc.rva;
855     mdDir.Location.DataSize = dump_misc_info(&dc);
856     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
857             &mdDir, sizeof(mdDir));
858
859     /* 3.2) write exception information (if any) */
860     if (ExceptionParam)
861     {
862         mdDir.StreamType = ExceptionStream;
863         mdDir.Location.Rva = dc.rva;
864         mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
865         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
866                 &mdDir, sizeof(mdDir));
867     }
868
869     /* 3.3) write user defined streams (if any) */
870     if (UserStreamParam)
871     {
872         for (i = 0; i < UserStreamParam->UserStreamCount; i++)
873         {
874             mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
875             mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
876             mdDir.Location.Rva = dc.rva;
877             writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
878                     &mdDir, sizeof(mdDir));
879             append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 
880                    UserStreamParam->UserStreamArray[i].BufferSize);
881         }
882     }
883
884     /* fill the remaining directory entries with 0's (unused stream types) */
885     /* NOTE: this should always come last in the dump! */
886     for (i = idx_stream; i < nStreams; i++)
887         writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
888
889     HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
890     HeapFree(GetProcessHeap(), 0, dc.mem);
891     HeapFree(GetProcessHeap(), 0, dc.modules);
892
893     return TRUE;
894 }
895
896 /******************************************************************
897  *              MiniDumpReadDumpStream (DEBUGHLP.@)
898  *
899  *
900  */
901 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
902                                    PMINIDUMP_DIRECTORY* pdir,
903                                    PVOID* stream, ULONG* size)
904 {
905     MINIDUMP_HEADER*    mdHead = (MINIDUMP_HEADER*)base;
906
907     if (mdHead->Signature == MINIDUMP_SIGNATURE)
908     {
909         MINIDUMP_DIRECTORY* dir;
910         int                 i;
911
912         dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
913         for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
914         {
915             if (dir->StreamType == str_idx)
916             {
917                 *pdir = dir;
918                 *stream = (char*)base + dir->Location.Rva;
919                 *size = dir->Location.DataSize;
920                 return TRUE;
921             }
922         }
923     }
924     SetLastError(ERROR_INVALID_PARAMETER);
925     return FALSE;
926 }