tgt_minidump.c 18.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Wine debugger - minidump handling
 *
 * Copyright 2005 Eric Pouech
 *
 * 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
 */

#define NONAMELESSUNION
#define NONAMELESSSTRUCT

#include "config.h"
25
#include "wine/port.h"
26 27 28 29 30 31 32 33 34 35 36

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "debugger.h"
#include "wingdi.h"
#include "winuser.h"
#include "tlhelp32.h"
#include "wine/debug.h"
37
#include "wine/exception.h"
38 39 40

WINE_DEFAULT_DEBUG_CHANNEL(winedbg);

41 42
static struct be_process_io be_process_minidump_io;

43 44 45 46 47 48 49 50 51
/* we need this function on 32bit hosts to ensure we zero out the higher DWORD
 * stored in the minidump file (sometimes it's not cleared, or the conversion from
 * 32bit to 64bit wide integers is done as signed, which is wrong)
 * So we clamp on 32bit CPUs (as stored in minidump information) all addresses to
 * keep only the lower 32 bits.
 * FIXME: as of today, since we don't support a backend CPU which is different from
 * CPU this process is running on, casting to (DWORD_PTR) will do just fine.
 */
static inline DWORD64  get_addr64(DWORD64 addr)
52
{
53
    return (DWORD_PTR)addr;
54 55
}

56 57 58 59 60 61
void minidump_write(const char* file, const EXCEPTION_RECORD* rec)
{
    HANDLE                              hFile;
    MINIDUMP_EXCEPTION_INFORMATION      mei;
    EXCEPTION_POINTERS                  ep;

62 63
    hFile = CreateFileA(file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL, NULL);
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

    if (hFile == INVALID_HANDLE_VALUE) return;

    if (rec)
    {
        mei.ThreadId = dbg_curr_thread->tid;
        mei.ExceptionPointers = &ep;
        ep.ExceptionRecord = (EXCEPTION_RECORD*)rec;
        ep.ContextRecord = &dbg_context;
        mei.ClientPointers = FALSE;
    }
    MiniDumpWriteDump(dbg_curr_process->handle, dbg_curr_process->pid,
                      hFile, MiniDumpNormal/*|MiniDumpWithDataSegs*/,
                      rec ? &mei : NULL, NULL, NULL);
    CloseHandle(hFile);
}
80 81 82 83 84 85 86 87 88 89

#define Wine_ElfModuleListStream        0xFFF0

struct tgt_process_minidump_data
{
    void*       mapping;
    HANDLE      hFile;
    HANDLE      hMap;
};

90
static inline struct tgt_process_minidump_data* private_data(struct dbg_process* pcs)
91
{
92
    return pcs->pio_data;
93 94
}

95 96
static BOOL tgt_process_minidump_read(HANDLE hProcess, const void* addr,
                                      void* buffer, SIZE_T len, SIZE_T* rlen)
97 98 99
{
    void*               stream;

100 101
    if (!private_data(dbg_curr_process)->mapping) return FALSE;
    if (MiniDumpReadDumpStream(private_data(dbg_curr_process)->mapping,
102
                               MemoryListStream, NULL, &stream, NULL))
103
    {
104
        MINIDUMP_MEMORY_LIST*   mml = stream;
105
        MINIDUMP_MEMORY_DESCRIPTOR* mmd = mml->MemoryRanges;
106 107 108 109 110 111 112 113
        int                     i, found = -1;
        SIZE_T                  ilen, prev_len = 0;

        /* There's no reason that memory ranges inside a minidump do not overlap.
         * So be smart when looking for a given memory range (either grab a
         * range that covers the whole requested area, or if none, the range that
         * has the largest overlap with requested area)
         */
114 115
        for (i = 0; i < mml->NumberOfMemoryRanges; i++, mmd++)
        {
116 117
            if (get_addr64(mmd->StartOfMemoryRange) <= (DWORD_PTR)addr &&
                (DWORD_PTR)addr < get_addr64(mmd->StartOfMemoryRange) + mmd->Memory.DataSize)
118
            {
119 120 121 122 123 124 125 126 127 128 129 130 131
                ilen = min(len,
                           get_addr64(mmd->StartOfMemoryRange) + mmd->Memory.DataSize - (DWORD_PTR)addr);
                if (ilen == len) /* whole range is matched */
                {
                    found = i;
                    prev_len = ilen;
                    break;
                }
                if (found == -1 || ilen > prev_len) /* partial match, keep largest one */
                {
                    found = i;
                    prev_len = ilen;
                }
132 133
            }
        }
134 135 136 137 138 139 140 141 142
        if (found != -1)
        {
            mmd = &mml->MemoryRanges[found];
            memcpy(buffer,
                   (char*)private_data(dbg_curr_process)->mapping + mmd->Memory.Rva + (DWORD_PTR)addr - get_addr64(mmd->StartOfMemoryRange),
                   prev_len);
            if (rlen) *rlen = prev_len;
            return TRUE;
        }
143 144 145 146 147 148 149 150 151 152 153 154 155 156
    }
    /* FIXME: this is a dirty hack to let the last frame in a bt to work
     * However, we need to check who's to blame, this code or the current 
     * dbghelp!StackWalk implementation
     */
    if ((DWORD_PTR)addr < 32)
    {
        memset(buffer, 0, len); 
        if (rlen) *rlen = len;
        return TRUE;
    }
    return FALSE;
}

157 158
static BOOL tgt_process_minidump_write(HANDLE hProcess, void* addr,
                                       const void* buffer, SIZE_T len, SIZE_T* wlen)
159 160 161 162
{
    return FALSE;
}

163
static BOOL CALLBACK validate_file(PCWSTR name, void* user)
164 165 166 167
{
    return FALSE; /* get the first file we find !! */
}

168 169 170 171 172
static BOOL is_pe_module_embedded(struct tgt_process_minidump_data* data,
                                  MINIDUMP_MODULE* pe_mm)
{
    MINIDUMP_MODULE_LIST*       mml;

173 174
    if (MiniDumpReadDumpStream(data->mapping, Wine_ElfModuleListStream, NULL,
                               (void**)&mml, NULL))
175 176 177 178
    {
        MINIDUMP_MODULE*        mm;
        unsigned                i;

179
        for (i = 0, mm = mml->Modules; i < mml->NumberOfModules; i++, mm++)
180
        {
181 182
            if (get_addr64(mm->BaseOfImage) <= get_addr64(pe_mm->BaseOfImage) &&
                get_addr64(mm->BaseOfImage) + mm->SizeOfImage >= get_addr64(pe_mm->BaseOfImage) + pe_mm->SizeOfImage)
183 184 185 186 187 188
                return TRUE;
        }
    }
    return FALSE;
}

189 190 191 192 193 194 195 196 197
static enum dbg_start minidump_do_reload(struct tgt_process_minidump_data* data)
{
    void*                       stream;
    DWORD                       pid = 1; /* by default */
    HANDLE                      hProc = (HANDLE)0x900DBAAD;
    int                         i;
    MINIDUMP_MODULE_LIST*       mml;
    MINIDUMP_MODULE*            mm;
    MINIDUMP_STRING*            mds;
198
    MINIDUMP_DIRECTORY*         dir;
199
    WCHAR                       exec_name[1024];
200
    WCHAR                       nameW[1024];
201
    unsigned                    len;
202
    static WCHAR                default_exec_name[] = {'<','m','i','n','i','d','u','m','p','-','e','x','e','c','>',0};
203 204

    /* fetch PID */
205
    if (MiniDumpReadDumpStream(data->mapping, MiscInfoStream, NULL, &stream, NULL))
206
    {
207
        MINIDUMP_MISC_INFO* mmi = stream;
208 209 210 211 212
        if (mmi->Flags1 & MINIDUMP_MISC1_PROCESS_ID)
            pid = mmi->ProcessId;
    }

    /* fetch executable name (it's normally the first one in module list) */
213
    lstrcpyW(exec_name, default_exec_name);
214
    if (MiniDumpReadDumpStream(data->mapping, ModuleListStream, NULL, &stream, NULL))
215
    {
216
        mml = stream;
217 218
        if (mml->NumberOfModules)
        {
219
            WCHAR*      ptr;
220

221
            mm = mml->Modules;
222
            mds = (MINIDUMP_STRING*)((char*)data->mapping + mm->ModuleNameRva);
223 224
            len = mds->Length / 2;
            memcpy(exec_name, mds->Buffer, mds->Length);
225 226
            exec_name[len] = 0;
            for (ptr = exec_name + len - 1; ptr >= exec_name; ptr--)
227 228 229
            {
                if (*ptr == '/' || *ptr == '\\')
                {
230
                    memmove(exec_name, ptr + 1, (lstrlenW(ptr + 1) + 1) * sizeof(WCHAR));
231 232 233 234 235 236
                    break;
                }
            }
        }
    }

237
    if (MiniDumpReadDumpStream(data->mapping, SystemInfoStream, &dir, &stream, NULL))
238
    {
239
        MINIDUMP_SYSTEM_INFO*   msi = stream;
240 241
        const char *str;
        char tmp[128];
242

243
        dbg_printf("WineDbg starting on minidump on pid %04x\n", pid);
244 245
        switch (msi->ProcessorArchitecture)
        {
246
        case PROCESSOR_ARCHITECTURE_UNKNOWN:
247 248 249 250 251 252
            str = "Unknown";
            break;
        case PROCESSOR_ARCHITECTURE_INTEL:
            strcpy(tmp, "Intel ");
            switch (msi->ProcessorLevel)
            {
253 254 255 256 257
            case  3: str = "80386"; break;
            case  4: str = "80486"; break;
            case  5: str = "Pentium"; break;
            case  6: str = "Pentium Pro/II or AMD Athlon"; break;
            case 15: str = "Pentium 4 or AMD Athlon64"; break;
258 259 260 261 262
            default: str = "???"; break;
            }
            strcat(tmp, str);
            if (msi->ProcessorLevel == 3 || msi->ProcessorLevel == 4)
            {
263
                if (HIBYTE(msi->ProcessorRevision) == 0xFF)
264
                    sprintf(tmp + strlen(tmp), " (%c%d)",
265 266
                            'A' + ((msi->ProcessorRevision>>4)&0xf)-0x0a,
                            ((msi->ProcessorRevision&0xf)));
267
                else
268
                    sprintf(tmp + strlen(tmp), " (%c%d)",
269 270
                            'A' + HIBYTE(msi->ProcessorRevision),
                            LOBYTE(msi->ProcessorRevision));
271
            }
272
            else sprintf(tmp + strlen(tmp), " (%d.%d)",
273 274
                         HIBYTE(msi->ProcessorRevision),
                         LOBYTE(msi->ProcessorRevision));
275 276 277 278 279 280 281 282 283 284 285
            str = tmp;
            break;
        case PROCESSOR_ARCHITECTURE_MIPS:
            str = "Mips";
            break;
        case PROCESSOR_ARCHITECTURE_ALPHA:
            str = "Alpha";
            break;
        case PROCESSOR_ARCHITECTURE_PPC:
            str = "PowerPC";
            break;
286 287 288
        case PROCESSOR_ARCHITECTURE_AMD64:
            str = "X86_64";
            break;
289 290 291
        case PROCESSOR_ARCHITECTURE_ARM:
            str = "ARM";
            break;
292 293 294 295 296
        default:
            str = "???";
            break;
        }
        dbg_printf("  %s was running on #%d %s CPU%s",
297
                   dbg_W2A(exec_name, -1), msi->u.s.NumberOfProcessors, str,
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
                   msi->u.s.NumberOfProcessors < 2 ? "" : "s");
        switch (msi->MajorVersion)
        {
        case 3:
            switch (msi->MinorVersion)
            {
            case 51: str = "NT 3.51"; break;
            default: str = "3-????"; break;
            }
            break;
        case 4:
            switch (msi->MinorVersion)
            {
            case 0: str = (msi->PlatformId == VER_PLATFORM_WIN32_NT) ? "NT 4.0" : "95"; break;
            case 10: str = "98"; break;
            case 90: str = "ME"; break;
            default: str = "5-????"; break;
            }
            break;
        case 5:
            switch (msi->MinorVersion)
            {
            case 0: str = "2000"; break;
            case 1: str = "XP"; break;
            case 2: str = "Server 2003"; break;
            default: str = "5-????"; break;
            }
            break;
        default: str = "???"; break;
        }
328
        dbg_printf(" on Windows %s (%u)\n", str, msi->BuildNumber);
329
        /* FIXME CSD: msi->CSDVersionRva */
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

        if (sizeof(MINIDUMP_SYSTEM_INFO) + 4 > dir->Location.DataSize &&
            msi->CSDVersionRva >= dir->Location.Rva + sizeof(MINIDUMP_SYSTEM_INFO) + 4)
        {
            const char*     code = (const char*)stream + sizeof(MINIDUMP_SYSTEM_INFO);
            const DWORD*    wes;

            if (code[0] == 'W' && code[1] == 'I' && code[2] == 'N' && code[3] == 'E' &&
                *(wes = (const DWORD*)(code += 4)) >= 3)
            {
                /* assume we have wine extensions */
                dbg_printf("    [on %s, on top of %s (%s)]\n",
                           code + wes[1], code + wes[2], code + wes[3]);
            }
        }
345 346 347 348 349 350 351
    }

    dbg_curr_process = dbg_add_process(&be_process_minidump_io, pid, hProc);
    dbg_curr_pid = pid;
    dbg_curr_process->pio_data = data;
    dbg_set_process_name(dbg_curr_process, exec_name);

352
    dbg_init(hProc, NULL, FALSE);
353

354
    if (MiniDumpReadDumpStream(data->mapping, ThreadListStream, NULL, &stream, NULL))
355
    {
356
        MINIDUMP_THREAD_LIST*   mtl = stream;
357
        ULONG                   i;
358

359 360 361
        for (i = 0; i < mtl->NumberOfThreads; i++)
        {
            dbg_add_thread(dbg_curr_process, mtl->Threads[i].ThreadId, NULL,
362
                           (void*)(DWORD_PTR)get_addr64(mtl->Threads[i].Teb));
363
        }
364 365
    }
    /* first load ELF modules, then do the PE ones */
366 367
    if (MiniDumpReadDumpStream(data->mapping, Wine_ElfModuleListStream, NULL,
                               &stream, NULL))
368
    {
369
        WCHAR   buffer[MAX_PATH];
370

371
        mml = stream;
372
        for (i = 0, mm = mml->Modules; i < mml->NumberOfModules; i++, mm++)
373 374
        {
            mds = (MINIDUMP_STRING*)((char*)data->mapping + mm->ModuleNameRva);
375 376 377 378
            memcpy(nameW, mds->Buffer, mds->Length);
            nameW[mds->Length / sizeof(WCHAR)] = 0;
            if (SymFindFileInPathW(hProc, NULL, nameW, (void*)(DWORD_PTR)mm->CheckSum,
                                   0, 0, SSRVOPT_DWORD, buffer, validate_file, NULL))
379 380
                dbg_load_module(hProc, NULL, buffer, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
381
            else
382 383
                SymLoadModuleExW(hProc, NULL, nameW, NULL, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage, NULL, SLMFLAG_VIRTUAL);
384 385
        }
    }
386
    if (MiniDumpReadDumpStream(data->mapping, ModuleListStream, NULL, &stream, NULL))
387
    {
388 389
        WCHAR   buffer[MAX_PATH];

390
        mml = stream;
391
        for (i = 0, mm = mml->Modules; i < mml->NumberOfModules; i++, mm++)
392 393
        {
            mds = (MINIDUMP_STRING*)((char*)data->mapping + mm->ModuleNameRva);
394 395
            memcpy(nameW, mds->Buffer, mds->Length);
            nameW[mds->Length / sizeof(WCHAR)] = 0;
396 397
            if (SymFindFileInPathW(hProc, NULL, nameW, (void*)(DWORD_PTR)mm->TimeDateStamp,
                                   mm->SizeOfImage, 0, SSRVOPT_DWORD, buffer, validate_file, NULL))
398 399
                dbg_load_module(hProc, NULL, buffer, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
400
            else if (is_pe_module_embedded(data, mm))
401 402
                dbg_load_module(hProc, NULL, nameW, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
403
            else
404 405
                SymLoadModuleExW(hProc, NULL, nameW, NULL, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage, NULL, SLMFLAG_VIRTUAL);
406 407
        }
    }
408
    if (MiniDumpReadDumpStream(data->mapping, ExceptionStream, NULL, &stream, NULL))
409
    {
410
        MINIDUMP_EXCEPTION_STREAM*      mes = stream;
411 412 413

        if ((dbg_curr_thread = dbg_get_thread(dbg_curr_process, mes->ThreadId)))
        {
414
            ADDRESS64   addr;
415 416 417 418 419

            dbg_curr_tid = mes->ThreadId;
            dbg_curr_thread->in_exception = TRUE;
            dbg_curr_thread->excpt_record.ExceptionCode = mes->ExceptionRecord.ExceptionCode;
            dbg_curr_thread->excpt_record.ExceptionFlags = mes->ExceptionRecord.ExceptionFlags;
420 421
            dbg_curr_thread->excpt_record.ExceptionRecord = (void*)(DWORD_PTR)get_addr64(mes->ExceptionRecord.ExceptionRecord);
            dbg_curr_thread->excpt_record.ExceptionAddress = (void*)(DWORD_PTR)get_addr64(mes->ExceptionRecord.ExceptionAddress);
422 423 424 425 426 427 428 429
            dbg_curr_thread->excpt_record.NumberParameters = mes->ExceptionRecord.NumberParameters;
            for (i = 0; i < dbg_curr_thread->excpt_record.NumberParameters; i++)
            {
                dbg_curr_thread->excpt_record.ExceptionInformation[i] = mes->ExceptionRecord.ExceptionInformation[i];
            }
            memcpy(&dbg_context, (char*)data->mapping + mes->ThreadContext.Rva,
                   min(sizeof(dbg_context), mes->ThreadContext.DataSize));
            memory_get_current_pc(&addr);
430
            stack_fetch_frames(&dbg_context);
431
            be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0);
432
            stack_info(-1);
433 434 435 436 437 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 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
            be_cpu->print_segment_info(dbg_curr_thread->handle, &dbg_context);
            stack_backtrace(mes->ThreadId);
            source_list_from_addr(&addr, 0);
        }
    }
    return start_ok;
}

static void cleanup(struct tgt_process_minidump_data* data)
{
    if (data->mapping)                          UnmapViewOfFile(data->mapping);
    if (data->hMap)                             CloseHandle(data->hMap);
    if (data->hFile != INVALID_HANDLE_VALUE)    CloseHandle(data->hFile);
    HeapFree(GetProcessHeap(), 0, data);
}

static struct be_process_io be_process_minidump_io;

enum dbg_start minidump_reload(int argc, char* argv[])
{
    struct tgt_process_minidump_data*   data;
    enum dbg_start                      ret = start_error_parse;

    /* try the form <myself> minidump-file */
    if (argc != 1) return start_error_parse;
    
    WINE_TRACE("Processing Minidump file %s\n", argv[0]);

    data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct tgt_process_minidump_data));
    if (!data) return start_error_init;
    data->mapping = NULL;
    data->hMap    = NULL;
    data->hFile   = INVALID_HANDLE_VALUE;

    if ((data->hFile = CreateFileA(argv[0], GENERIC_READ, FILE_SHARE_READ, NULL, 
                                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE &&
        ((data->hMap = CreateFileMappingA(data->hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != 0) &&
        ((data->mapping = MapViewOfFile(data->hMap, FILE_MAP_READ, 0, 0, 0)) != NULL))
    {
        __TRY
        {
            if (((MINIDUMP_HEADER*)data->mapping)->Signature == MINIDUMP_SIGNATURE)
            {
                ret = minidump_do_reload(data);
            }
        }
        __EXCEPT_PAGE_FAULT
        {
            dbg_printf("Unexpected fault while reading minidump %s\n", argv[0]);
            dbg_curr_pid = 0;
        }
        __ENDTRY;
    }
    if (ret != start_ok) cleanup(data);
    return ret;
}

static BOOL tgt_process_minidump_close_process(struct dbg_process* pcs, BOOL kill)
{
492
    struct tgt_process_minidump_data*    data = private_data(pcs);
493 494 495 496 497 498 499 500

    cleanup(data);
    pcs->pio_data = NULL;
    SymCleanup(pcs->handle);
    dbg_del_process(pcs);
    return TRUE;
}

501
static BOOL tgt_process_minidump_get_selector(HANDLE hThread, DWORD sel, LDT_ENTRY* le)
502 503 504 505 506 507 508
{
    /* so far, pretend all selectors are valid, and mapped to a 32bit flat address space */
    memset(le, 0, sizeof(*le));
    le->HighWord.Bits.Default_Big = 1;
    return TRUE;
}

509 510 511 512 513
static struct be_process_io be_process_minidump_io =
{
    tgt_process_minidump_close_process,
    tgt_process_minidump_read,
    tgt_process_minidump_write,
514
    tgt_process_minidump_get_selector,
515
};