minidump.c 36.5 KB
Newer Older
1 2 3
/*
 * File minidump.c - management of dumps (read & write)
 *
4
 * Copyright (C) 2004-2005, Eric Pouech
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23 24 25
 */

#include <time.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT

26
#include "ntstatus.h"
27 28
#define WIN32_NO_STATUS
#include "dbghelp_private.h"
29 30
#include "winternl.h"
#include "psapi.h"
31 32 33 34
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);

35 36 37 38 39 40 41 42 43 44 45 46
struct dump_memory
{
    ULONG                               base;
    ULONG                               size;
    ULONG                               rva;
};

struct dump_module
{
    unsigned                            is_elf;
    ULONG                               base;
    ULONG                               size;
47 48
    DWORD                               timestamp;
    DWORD                               checksum;
49
    WCHAR                               name[MAX_PATH];
50 51 52 53 54 55 56 57 58 59
};

struct dump_context
{
    /* process & thread information */
    HANDLE                              hProcess;
    DWORD                               pid;
    void*                               pcs_buffer;
    SYSTEM_PROCESS_INFORMATION*         spi;
    /* module information */
60 61
    struct dump_module*                 modules;
    unsigned                            num_modules;
62 63 64 65 66 67 68 69 70 71 72 73
    /* exception information */
    /* output information */
    MINIDUMP_TYPE                       type;
    HANDLE                              hFile;
    RVA                                 rva;
    struct dump_memory*                 mem;
    unsigned                            num_mem;
    /* callback information */
    MINIDUMP_CALLBACK_INFORMATION*      cb;
};

/******************************************************************
74
 *		fetch_processes_info
75 76 77 78
 *
 * reads system wide process information, and make spi point to the record
 * for process of id 'pid'
 */
79
static BOOL fetch_processes_info(struct dump_context* dc)
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
    ULONG       buf_size = 0x1000;
    NTSTATUS    nts;

    dc->pcs_buffer = NULL;
    if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
    for (;;)
    {
        nts = NtQuerySystemInformation(SystemProcessInformation, 
                                       dc->pcs_buffer, buf_size, NULL);
        if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
        dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer, 
                                     buf_size *= 2);
        if (!dc->pcs_buffer) return FALSE;
    }

    if (nts == STATUS_SUCCESS)
    {
        dc->spi = dc->pcs_buffer;
        for (;;)
        {
101 102 103
            if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
            if (!dc->spi->NextEntryOffset) break;
            dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
104 105 106 107 108 109 110 111
        }
    }
    HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
    dc->pcs_buffer = NULL;
    dc->spi = NULL;
    return FALSE;
}

112
static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
113 114 115 116 117 118 119 120
                               const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
{
    NT_TIB      tib;

    if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
    {
#ifdef __i386__
        /* limiting the stack dumping to the size actually used */
121 122 123 124 125 126 127 128 129 130 131
        if (ctx->Esp){

            /* make sure ESP is within the established range of the stack.  It could have
               been clobbered by whatever caused the original exception. */
            if (ctx->Esp - 4 < (ULONG_PTR)tib.StackLimit || ctx->Esp - 4 > (ULONG_PTR)tib.StackBase)
                mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;

            else
                mmd->StartOfMemoryRange = (ctx->Esp - 4);
        }

132 133
        else
            mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
134

135
#elif defined(__powerpc__)
136 137 138 139 140 141 142 143 144 145 146
        if (ctx->Iar){

            /* make sure IAR is within the established range of the stack.  It could have
               been clobbered by whatever caused the original exception. */
            if (ctx->Iar - 4 < (ULONG_PTR)tib.StackLimit || ctx->Iar - 4 > (ULONG_PTR)tib.StackBase)
                mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;

            else
                mmd->StartOfMemoryRange = (ctx->Iar - 4);
        }

147 148
        else
            mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
149

150
#elif defined(__x86_64__)
151 152 153 154 155 156 157 158 159 160 161
        if (ctx->Rsp){

            /* make sure RSP is within the established range of the stack.  It could have
               been clobbered by whatever caused the original exception. */
            if (ctx->Rsp - 8 < (ULONG_PTR)tib.StackLimit || ctx->Rsp - 8 > (ULONG_PTR)tib.StackBase)
                mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;

            else
                mmd->StartOfMemoryRange = (ctx->Rsp - 8);
        }

162 163
        else
            mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
164

165 166 167 168 169 170 171
#else
#error unsupported CPU
#endif
        mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
    }
}

172 173 174 175 176 177
/******************************************************************
 *		fetch_thread_info
 *
 * fetches some information about thread of id 'tid'
 */
static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
178
                              const MINIDUMP_EXCEPTION_INFORMATION* except,
179 180
                              MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
{
181
    DWORD                       tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
182 183 184 185 186
    HANDLE                      hThread;
    THREAD_BASIC_INFORMATION    tbi;

    memset(ctx, 0, sizeof(*ctx));

187
    mdThd->ThreadId = tid;
188 189 190 191 192 193 194 195 196 197 198 199
    mdThd->SuspendCount = 0;
    mdThd->Teb = 0;
    mdThd->Stack.StartOfMemoryRange = 0;
    mdThd->Stack.Memory.DataSize = 0;
    mdThd->Stack.Memory.Rva = 0;
    mdThd->ThreadContext.DataSize = 0;
    mdThd->ThreadContext.Rva = 0;
    mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
    mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;

    if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
    {
200
        FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
201 202 203 204 205 206 207
        return FALSE;
    }
    
    if (NtQueryInformationThread(hThread, ThreadBasicInformation,
                                 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
    {
        mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
208
        if (tbi.ExitStatus == STILL_ACTIVE)
209
        {
210 211 212 213 214 215
            if (tid != GetCurrentThreadId() &&
                (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
            {
                ctx->ContextFlags = CONTEXT_FULL;
                if (!GetThreadContext(hThread, ctx))
                    memset(ctx, 0, sizeof(*ctx));
216

217 218 219 220
                fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
                ResumeThread(hThread);
            }
            else if (tid == GetCurrentThreadId() && except)
221
            {
222
                CONTEXT lctx, *pctx;
223
                mdThd->SuspendCount = 1;
224 225 226 227 228 229 230 231 232 233 234
                if (except->ClientPointers)
                {
                    EXCEPTION_POINTERS      ep;

                    ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
                                      &ep, sizeof(ep), NULL);
                    ReadProcessMemory(dc->hProcess, ep.ContextRecord,
                                      &ctx, sizeof(ctx), NULL);
                    pctx = &lctx;
                }
                else pctx = except->ExceptionPointers->ContextRecord;
235

236
                *ctx = *pctx;
237
                fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
238
            }
239
            else mdThd->SuspendCount = 0;
240 241 242 243 244 245 246 247 248 249 250
        }
    }
    CloseHandle(hThread);
    return TRUE;
}

/******************************************************************
 *		add_module
 *
 * Add a module to a dump context
 */
251
static BOOL add_module(struct dump_context* dc, const WCHAR* name,
252 253
                       DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
                       BOOL is_elf)
254
{
255 256 257
    if (!dc->modules)
        dc->modules = HeapAlloc(GetProcessHeap(), 0,
                                ++dc->num_modules * sizeof(*dc->modules));
258
    else
259 260 261
        dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
                                  ++dc->num_modules * sizeof(*dc->modules));
    if (!dc->modules) return FALSE;
262
    if (is_elf ||
263
        !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
264 265 266 267 268 269 270 271 272
                              dc->modules[dc->num_modules - 1].name,
                              sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR)))
        lstrcpynW(dc->modules[dc->num_modules - 1].name, name,
                  sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR));
    dc->modules[dc->num_modules - 1].base = base;
    dc->modules[dc->num_modules - 1].size = size;
    dc->modules[dc->num_modules - 1].timestamp = timestamp;
    dc->modules[dc->num_modules - 1].checksum = checksum;
    dc->modules[dc->num_modules - 1].is_elf = is_elf;
273 274 275 276 277 278 279 280 281

    return TRUE;
}

/******************************************************************
 *		fetch_pe_module_info_cb
 *
 * Callback for accumulating in dump_context a PE modules set
 */
282 283
static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
                                           PVOID user)
284
{
285
    struct dump_context*        dc = user;
286 287
    IMAGE_NT_HEADERS            nth;

288 289
    if (!validate_addr64(base)) return FALSE;

290
    if (pe_load_nt_header(dc->hProcess, base, &nth))
291
        add_module(user, name, base, size,
292 293 294
                   nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
                   FALSE);
    return TRUE;
295 296 297 298 299
}

/******************************************************************
 *		fetch_elf_module_info_cb
 *
300
 * Callback for accumulating in dump_context an ELF modules set
301
 */
302
static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
303 304
                                     void* user)
{
305
    struct dump_context*        dc = user;
306
    DWORD                       rbase, size, checksum;
307 308 309 310 311 312

    /* FIXME: there's no relevant timestamp on ELF modules */
    /* NB: if we have a non-null base from the live-target use it (whenever
     * the ELF module is relocatable or not). If we have a null base (ELF
     * module isn't relocatable) then grab its base address from ELF file
     */
313
    if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
314
        size = checksum = 0;
315
    add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
316
    return TRUE;
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
/******************************************************************
 *		fetch_macho_module_info_cb
 *
 * Callback for accumulating in dump_context a Mach-O modules set
 */
static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
                                       void* user)
{
    struct dump_context*        dc = (struct dump_context*)user;
    DWORD                       rbase, size, checksum;

    /* FIXME: there's no relevant timestamp on Mach-O modules */
    /* NB: if we have a non-null base from the live-target use it.  If we have
     * a null base, then grab its base address from Mach-O file.
     */
    if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
        size = checksum = 0;
    add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
    return TRUE;
}

340
static void fetch_modules_info(struct dump_context* dc)
341
{
342
    EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
343 344 345 346 347 348
    /* Since we include ELF modules in a separate stream from the regular PE ones,
     * we can always include those ELF modules (they don't eat lots of space)
     * And it's always a good idea to have a trace of the loaded ELF modules for
     * a given application in a post mortem debugging condition.
     */
    elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
349
    macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
350 351
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
{
    DWORD       handle;
    DWORD       sz;
    static const WCHAR backslashW[] = {'\\', '\0'};

    memset(ffi, 0, sizeof(*ffi));
    if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
    {
        void*   info = HeapAlloc(GetProcessHeap(), 0, sz);
        if (info && GetFileVersionInfoW(filename, handle, sz, info))
        {
            VS_FIXEDFILEINFO*   ptr;
            UINT    len;

            if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
                memcpy(ffi, ptr, min(len, sizeof(*ffi)));
        }
        HeapFree(GetProcessHeap(), 0, info);
    }
}

374 375 376 377 378 379 380
/******************************************************************
 *		add_memory_block
 *
 * Add a memory block to be dumped in a minidump
 * If rva is non 0, it's the rva in the minidump where has to be stored
 * also the rva of the memory block when written (this allows to reference
 * a memory block from outside the list of memory blocks).
381
 */
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
{
    if (dc->mem)
        dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 
                              ++dc->num_mem * sizeof(*dc->mem));
    else
        dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
    if (dc->mem)
    {
        dc->mem[dc->num_mem - 1].base = base;
        dc->mem[dc->num_mem - 1].size = size;
        dc->mem[dc->num_mem - 1].rva  = rva;
    }
    else dc->num_mem = 0;
}

/******************************************************************
 *		writeat
 *
 * Writes a chunk of data at a given position in the minidump
 */
403
static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
{
    DWORD       written;

    SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
    WriteFile(dc->hFile, data, size, &written, NULL);
}

/******************************************************************
 *		append
 *
 * writes a new chunk of data to the minidump, increasing the current
 * rva in dc
 */
static void append(struct dump_context* dc, void* data, unsigned size)
{
    writeat(dc, dc->rva, data, size);
    dc->rva += size;
}

/******************************************************************
 *		dump_exception_info
 *
 * Write in File the exception information from pcs
 */
428 429
static  unsigned        dump_exception_info(struct dump_context* dc,
                                            const MINIDUMP_EXCEPTION_INFORMATION* except)
430 431
{
    MINIDUMP_EXCEPTION_STREAM   mdExcpt;
432 433
    EXCEPTION_RECORD            rec, *prec;
    CONTEXT                     ctx, *pctx;
434
    DWORD                       i;
435

436
    mdExcpt.ThreadId = except->ThreadId;
437
    mdExcpt.__alignment = 0;
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    if (except->ClientPointers)
    {
        EXCEPTION_POINTERS      ep;

        ReadProcessMemory(dc->hProcess, 
                          except->ExceptionPointers, &ep, sizeof(ep), NULL);
        ReadProcessMemory(dc->hProcess, 
                          ep.ExceptionRecord, &rec, sizeof(rec), NULL);
        ReadProcessMemory(dc->hProcess, 
                          ep.ContextRecord, &ctx, sizeof(ctx), NULL);
        prec = &rec;
        pctx = &ctx;
    }
    else
    {
        prec = except->ExceptionPointers->ExceptionRecord;
        pctx = except->ExceptionPointers->ContextRecord;
    }
    mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
    mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
    mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
    mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
    mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
    mdExcpt.ExceptionRecord.__unusedAlignment = 0;
    for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
463
        mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
464 465 466 467 468
    mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
    mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);

    append(dc, &mdExcpt, sizeof(mdExcpt));
    append(dc, pctx, sizeof(*pctx));
469
    return sizeof(mdExcpt);
470 471 472 473 474 475 476
}

/******************************************************************
 *		dump_modules
 *
 * Write in File the modules from pcs
 */
477
static  unsigned        dump_modules(struct dump_context* dc, BOOL dump_elf)
478 479 480
{
    MINIDUMP_MODULE             mdModule;
    MINIDUMP_MODULE_LIST        mdModuleList;
481 482 483 484 485
    char                        tmp[1024];
    MINIDUMP_STRING*            ms = (MINIDUMP_STRING*)tmp;
    ULONG                       i, nmod;
    RVA                         rva_base;
    DWORD                       flags_out;
486
    unsigned                    sz;
487

488
    for (i = nmod = 0; i < dc->num_modules; i++)
489
    {
490 491
        if ((dc->modules[i].is_elf && dump_elf) ||
            (!dc->modules[i].is_elf && !dump_elf))
492
            nmod++;
493
    }
494 495 496 497 498 499 500

    mdModuleList.NumberOfModules = 0;
    /* reserve space for mdModuleList
     * FIXME: since we don't support 0 length arrays, we cannot use the
     * size of mdModuleList
     * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
     */
501 502 503 504 505

    /* the stream size is just the size of the module index.  It does not include the data for the
       names of each module.  *Technically* the names are supposed to go into the common string table
       in the minidump file.  Since each string is referenced by RVA they can all safely be located
       anywhere between streams in the file, so the end of this stream is sufficient. */
506
    rva_base = dc->rva;
507
    dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
508
    for (i = 0; i < dc->num_modules; i++)
509
    {
510 511
        if ((dc->modules[i].is_elf && !dump_elf) ||
            (!dc->modules[i].is_elf && dump_elf))
512 513 514 515 516 517 518 519 520
            continue;

        flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
        if (dc->type & MiniDumpWithDataSegs)
            flags_out |= ModuleWriteDataSeg;
        if (dc->type & MiniDumpWithProcessThreadData)
            flags_out |= ModuleWriteTlsData;
        if (dc->type & MiniDumpWithCodeSegs)
            flags_out |= ModuleWriteCodeSegs;
521
        ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
522 523
        if (sizeof(ULONG) + ms->Length > sizeof(tmp))
            FIXME("Buffer overflow!!!\n");
524
        lstrcpyW(ms->Buffer, dc->modules[i].name);
525 526 527 528 529 530 531 532 533 534 535

        if (dc->cb)
        {
            MINIDUMP_CALLBACK_INPUT     cbin;
            MINIDUMP_CALLBACK_OUTPUT    cbout;

            cbin.ProcessId = dc->pid;
            cbin.ProcessHandle = dc->hProcess;
            cbin.CallbackType = ModuleCallback;

            cbin.u.Module.FullPath = ms->Buffer;
536 537 538 539
            cbin.u.Module.BaseOfImage = dc->modules[i].base;
            cbin.u.Module.SizeOfImage = dc->modules[i].size;
            cbin.u.Module.CheckSum = dc->modules[i].checksum;
            cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
540 541 542 543 544 545 546 547 548 549 550 551 552
            memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
            cbin.u.Module.CvRecord = NULL;
            cbin.u.Module.SizeOfCvRecord = 0;
            cbin.u.Module.MiscRecord = NULL;
            cbin.u.Module.SizeOfMiscRecord = 0;

            cbout.u.ModuleWriteFlags = flags_out;
            if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
                continue;
            flags_out &= cbout.u.ModuleWriteFlags;
        }
        if (flags_out & ModuleWriteModule)
        {
553 554 555 556
            mdModule.BaseOfImage = dc->modules[i].base;
            mdModule.SizeOfImage = dc->modules[i].size;
            mdModule.CheckSum = dc->modules[i].checksum;
            mdModule.TimeDateStamp = dc->modules[i].timestamp;
557 558
            mdModule.ModuleNameRva = dc->rva;
            ms->Length -= sizeof(WCHAR);
559
            append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
560
            fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
561 562 563 564 565 566 567 568 569 570 571
            mdModule.CvRecord.DataSize = 0; /* FIXME */
            mdModule.CvRecord.Rva = 0; /* FIXME */
            mdModule.MiscRecord.DataSize = 0; /* FIXME */
            mdModule.MiscRecord.Rva = 0; /* FIXME */
            mdModule.Reserved0 = 0; /* FIXME */
            mdModule.Reserved1 = 0; /* FIXME */
            writeat(dc,
                    rva_base + sizeof(mdModuleList.NumberOfModules) + 
                        mdModuleList.NumberOfModules++ * sizeof(mdModule), 
                    &mdModule, sizeof(mdModule));
        }
572
    }
573 574
    writeat(dc, rva_base, &mdModuleList.NumberOfModules, 
            sizeof(mdModuleList.NumberOfModules));
575 576

    return sz;
577 578
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
/* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
 * We are compiled with -fPIC, so we can't clobber ebx.
 */
static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
{
#if defined(__GNUC__) && defined(__i386__)
    __asm__("pushl %%ebx\n\t"
            "cpuid\n\t"
            "movl %%ebx, %%esi\n\t"
            "popl %%ebx"
            : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
            :  "0" (ax));
#endif
}

/* From xf86info havecpuid.c 1.11 */
static inline int have_x86cpuid(void)
{
#if defined(__GNUC__) && defined(__i386__)
    unsigned int f1, f2;
    __asm__("pushfl\n\t"
            "pushfl\n\t"
            "popl %0\n\t"
            "movl %0,%1\n\t"
            "xorl %2,%0\n\t"
            "pushl %0\n\t"
            "popfl\n\t"
            "pushfl\n\t"
            "popl %0\n\t"
            "popfl"
            : "=&r" (f1), "=&r" (f2)
            : "ir" (0x00200000));
    return ((f1^f2) & 0x00200000) != 0;
#else
    return 0;
#endif
}

617 618 619 620 621
/******************************************************************
 *		dump_system_info
 *
 * Dumps into File the information about the system
 */
622
static  unsigned        dump_system_info(struct dump_context* dc)
623 624 625
{
    MINIDUMP_SYSTEM_INFO        mdSysInfo;
    SYSTEM_INFO                 sysInfo;
626
    OSVERSIONINFOW              osInfo;
627
    DWORD                       written;
628
    ULONG                       slen;
629 630

    GetSystemInfo(&sysInfo);
631 632
    osInfo.dwOSVersionInfoSize = sizeof(osInfo);
    GetVersionExW(&osInfo);
633 634 635 636

    mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
    mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
    mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
637 638
    mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
    mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
639 640 641 642 643
    mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
    mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
    mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
    mdSysInfo.PlatformId = osInfo.dwPlatformId;

644 645
    mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
    mdSysInfo.u1.Reserved1 = 0;
646
    mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
647

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
    if (have_x86cpuid())
    {
        unsigned        regs0[4], regs1[4];

        do_x86cpuid(0, regs0);
        mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
        mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
        mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
        do_x86cpuid(1, regs1);
        mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
        mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
        mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
        if (regs0[1] == 0x68747541 /* "Auth" */ &&
            regs0[3] == 0x69746e65 /* "enti" */ &&
            regs0[2] == 0x444d4163 /* "cAMD" */)
        {
            do_x86cpuid(0x80000000, regs1);  /* get vendor cpuid level */
            if (regs1[0] >= 0x80000001)
            {
                do_x86cpuid(0x80000001, regs1);  /* get vendor features */
                mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
            }
        }
    }
    else
    {
        unsigned        i;
        ULONG64         one = 1;
676

677 678 679 680 681 682 683
        mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
        mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;

        for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
            if (IsProcessorFeaturePresent(i))
                mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
    }
684
    append(dc, &mdSysInfo, sizeof(mdSysInfo));
685

686 687
    /* write the service pack version string after this stream.  It is referenced within the
       stream by its RVA in the file. */
688 689 690 691
    slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
    WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
    WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
    dc->rva += sizeof(ULONG) + slen;
692 693

    return sizeof(mdSysInfo);
694 695 696 697 698 699 700
}

/******************************************************************
 *		dump_threads
 *
 * Dumps into File the information about running threads
 */
701 702
static  unsigned        dump_threads(struct dump_context* dc,
                                     const MINIDUMP_EXCEPTION_INFORMATION* except)
703 704 705
{
    MINIDUMP_THREAD             mdThd;
    MINIDUMP_THREAD_LIST        mdThdList;
706 707 708 709 710 711 712 713 714 715 716 717 718
    unsigned                    i;
    RVA                         rva_base;
    DWORD                       flags_out;
    CONTEXT                     ctx;

    mdThdList.NumberOfThreads = 0;

    rva_base = dc->rva;
    dc->rva += sizeof(mdThdList.NumberOfThreads) +
        dc->spi->dwThreadCount * sizeof(mdThd);

    for (i = 0; i < dc->spi->dwThreadCount; i++)
    {
719
        fetch_thread_info(dc, i, except, &mdThd, &ctx);
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735

        flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
            ThreadWriteInstructionWindow;
        if (dc->type & MiniDumpWithProcessThreadData)
            flags_out |= ThreadWriteThreadData;
        if (dc->type & MiniDumpWithThreadInfo)
            flags_out |= ThreadWriteThreadInfo;

        if (dc->cb)
        {
            MINIDUMP_CALLBACK_INPUT     cbin;
            MINIDUMP_CALLBACK_OUTPUT    cbout;

            cbin.ProcessId = dc->pid;
            cbin.ProcessHandle = dc->hProcess;
            cbin.CallbackType = ThreadCallback;
736
            cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
737
            cbin.u.Thread.ThreadHandle = 0; /* FIXME */
738
            cbin.u.Thread.Context = ctx;
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
            cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
            cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
            cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
                mdThd.Stack.Memory.DataSize;

            cbout.u.ThreadWriteFlags = flags_out;
            if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
                continue;
            flags_out &= cbout.u.ThreadWriteFlags;
        }
        if (flags_out & ThreadWriteThread)
        {
            if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
            {
                mdThd.ThreadContext.Rva = dc->rva;
                mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
                append(dc, &ctx, sizeof(CONTEXT));
            }
            if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
            {
                add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
                                 mdThd.Stack.Memory.DataSize,
                                 rva_base + sizeof(mdThdList.NumberOfThreads) +
                                     mdThdList.NumberOfThreads * sizeof(mdThd) +
                                     FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
            }
            writeat(dc, 
                    rva_base + sizeof(mdThdList.NumberOfThreads) +
                        mdThdList.NumberOfThreads * sizeof(mdThd),
                    &mdThd, sizeof(mdThd));
            mdThdList.NumberOfThreads++;
        }
        if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
        {
            /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
             *        - also crop values across module boundaries, 
             *        - and don't make it i386 dependent 
             */
            /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
        }
    }
    writeat(dc, rva_base,
            &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
782 783

    return dc->rva - rva_base;
784 785 786 787 788 789 790
}

/******************************************************************
 *		dump_memory_info
 *
 * dumps information about the memory of the process (stack of the threads)
 */
791
static unsigned         dump_memory_info(struct dump_context* dc)
792 793 794
{
    MINIDUMP_MEMORY_LIST        mdMemList;
    MINIDUMP_MEMORY_DESCRIPTOR  mdMem;
795
    DWORD                       written;
796
    unsigned                    i, pos, len, sz;
797 798 799 800 801 802 803
    RVA                         rva_base;
    char                        tmp[1024];

    mdMemList.NumberOfMemoryRanges = dc->num_mem;
    append(dc, &mdMemList.NumberOfMemoryRanges,
           sizeof(mdMemList.NumberOfMemoryRanges));
    rva_base = dc->rva;
804 805 806
    sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
    dc->rva += sz;
    sz += sizeof(mdMemList.NumberOfMemoryRanges);
807 808

    for (i = 0; i < dc->num_mem; i++)
809
    {
810 811 812 813 814 815 816 817
        mdMem.StartOfMemoryRange = dc->mem[i].base;
        mdMem.Memory.Rva = dc->rva;
        mdMem.Memory.DataSize = dc->mem[i].size;
        SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
        for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
        {
            len = min(dc->mem[i].size - pos, sizeof(tmp));
            if (ReadProcessMemory(dc->hProcess, 
818
                                  (void*)(dc->mem[i].base + pos),
819 820 821 822 823 824 825 826 827
                                  tmp, len, NULL))
                WriteFile(dc->hFile, tmp, len, &written, NULL);
        }
        dc->rva += mdMem.Memory.DataSize;
        writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
        if (dc->mem[i].rva)
        {
            writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
        }
828
    }
829 830

    return sz;
831 832
}

833
static unsigned         dump_misc_info(struct dump_context* dc)
834 835 836 837 838 839 840
{
    MINIDUMP_MISC_INFO  mmi;

    mmi.SizeOfInfo = sizeof(mmi);
    mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
    mmi.ProcessId = dc->pid;
    /* FIXME: create/user/kernel time */
841 842 843 844
    mmi.ProcessCreateTime = 0;
    mmi.ProcessKernelTime = 0;
    mmi.ProcessUserTime = 0;

845
    append(dc, &mmi, sizeof(mmi));
846
    return sizeof(mmi);
847 848 849 850 851 852
}

/******************************************************************
 *		MiniDumpWriteDump (DEBUGHLP.@)
 *
 */
853
BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
854 855 856 857 858
                              MINIDUMP_TYPE DumpType,
                              PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
                              PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
                              PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
{
859
    static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
860 861
    MINIDUMP_HEADER     mdHead;
    MINIDUMP_DIRECTORY  mdDir;
862 863
    DWORD               i, nStreams, idx_stream;
    struct dump_context dc;
864

865 866 867
    dc.hProcess = hProcess;
    dc.hFile = hFile;
    dc.pid = pid;
868 869
    dc.modules = NULL;
    dc.num_modules = 0;
870 871 872 873 874
    dc.cb = CallbackParam;
    dc.type = DumpType;
    dc.mem = NULL;
    dc.num_mem = 0;
    dc.rva = 0;
875

876 877
    if (!fetch_processes_info(&dc)) return FALSE;
    fetch_modules_info(&dc);
878

879 880 881
    /* 1) init */
    nStreams = 6 + (ExceptionParam ? 1 : 0) +
        (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
882

883 884 885
    /* pad the directory size to a multiple of 4 for alignment purposes */
    nStreams = (nStreams + 3) & ~3;

886 887 888 889 890 891 892 893 894 895 896 897 898
    if (DumpType & MiniDumpWithDataSegs)
        FIXME("NIY MiniDumpWithDataSegs\n");
    if (DumpType & MiniDumpWithFullMemory)
        FIXME("NIY MiniDumpWithFullMemory\n");
    if (DumpType & MiniDumpWithHandleData)
        FIXME("NIY MiniDumpWithHandleData\n");
    if (DumpType & MiniDumpFilterMemory)
        FIXME("NIY MiniDumpFilterMemory\n");
    if (DumpType & MiniDumpScanMemory)
        FIXME("NIY MiniDumpScanMemory\n");

    /* 2) write header */
    mdHead.Signature = MINIDUMP_SIGNATURE;
899
    mdHead.Version = MINIDUMP_VERSION;  /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
900
    mdHead.NumberOfStreams = nStreams;
901
    mdHead.CheckSum = 0;                /* native sets a 0 checksum in its files */
902
    mdHead.StreamDirectoryRva = sizeof(mdHead);
903 904
    mdHead.u.TimeDateStamp = time(NULL);
    mdHead.Flags = DumpType;
905 906
    append(&dc, &mdHead, sizeof(mdHead));

907
    /* 3) write stream directories */
908 909 910
    dc.rva += nStreams * sizeof(mdDir);
    idx_stream = 0;

911
    /* 3.1) write data stream directories */
912

913 914 915
    /* must be first in minidump */
    mdDir.StreamType = SystemInfoStream;
    mdDir.Location.Rva = dc.rva;
916
    mdDir.Location.DataSize = dump_system_info(&dc);
917 918 919
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
            &mdDir, sizeof(mdDir));

920
    mdDir.StreamType = ThreadListStream;
921
    mdDir.Location.Rva = dc.rva;
922
    mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
923 924 925 926 927
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 
            &mdDir, sizeof(mdDir));

    mdDir.StreamType = ModuleListStream;
    mdDir.Location.Rva = dc.rva;
928
    mdDir.Location.DataSize = dump_modules(&dc, FALSE);
929 930 931 932 933
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
            &mdDir, sizeof(mdDir));

    mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
    mdDir.Location.Rva = dc.rva;
934
    mdDir.Location.DataSize = dump_modules(&dc, TRUE);
935 936 937 938 939
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
            &mdDir, sizeof(mdDir));

    mdDir.StreamType = MemoryListStream;
    mdDir.Location.Rva = dc.rva;
940
    mdDir.Location.DataSize = dump_memory_info(&dc);
941 942 943 944 945
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
            &mdDir, sizeof(mdDir));

    mdDir.StreamType = MiscInfoStream;
    mdDir.Location.Rva = dc.rva;
946
    mdDir.Location.DataSize = dump_misc_info(&dc);
947 948 949 950 951
    writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
            &mdDir, sizeof(mdDir));

    /* 3.2) write exception information (if any) */
    if (ExceptionParam)
952
    {
953 954
        mdDir.StreamType = ExceptionStream;
        mdDir.Location.Rva = dc.rva;
955
        mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
956 957
        writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
                &mdDir, sizeof(mdDir));
958 959
    }

960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    /* 3.3) write user defined streams (if any) */
    if (UserStreamParam)
    {
        for (i = 0; i < UserStreamParam->UserStreamCount; i++)
        {
            mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
            mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
            mdDir.Location.Rva = dc.rva;
            writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
                    &mdDir, sizeof(mdDir));
            append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 
                   UserStreamParam->UserStreamArray[i].BufferSize);
        }
    }

975 976 977 978 979
    /* fill the remaining directory entries with 0's (unused stream types) */
    /* NOTE: this should always come last in the dump! */
    for (i = idx_stream; i < nStreams; i++)
        writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));

980 981
    HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
    HeapFree(GetProcessHeap(), 0, dc.mem);
982
    HeapFree(GetProcessHeap(), 0, dc.modules);
983

984 985
    return TRUE;
}
986 987 988 989 990 991

/******************************************************************
 *		MiniDumpReadDumpStream (DEBUGHLP.@)
 *
 *
 */
992
BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
993
                                   PMINIDUMP_DIRECTORY* pdir,
994
                                   PVOID* stream, ULONG* size)
995
{
996
    MINIDUMP_HEADER*    mdHead = base;
997 998 999 1000

    if (mdHead->Signature == MINIDUMP_SIGNATURE)
    {
        MINIDUMP_DIRECTORY* dir;
1001
        DWORD               i;
1002 1003 1004 1005 1006 1007

        dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
        for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
        {
            if (dir->StreamType == str_idx)
            {
1008 1009 1010
                if (pdir) *pdir = dir;
                if (stream) *stream = (char*)base + dir->Location.Rva;
                if (size) *size = dir->Location.DataSize;
1011 1012 1013 1014 1015 1016 1017
                return TRUE;
            }
        }
    }
    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
}