tgt_minidump.c 20.4 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 const 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
        case PROCESSOR_ARCHITECTURE_ARM64:
            str = "ARM64";
            break;
295 296 297 298 299 300
        case PROCESSOR_ARCHITECTURE_MSIL:
            str = "MSIL";
            break;
        case PROCESSOR_ARCHITECTURE_NEUTRAL:
            str = "Neutral";
            break;
301 302 303 304 305
        default:
            str = "???";
            break;
        }
        dbg_printf("  %s was running on #%d %s CPU%s",
306
                   dbg_W2A(exec_name, -1), msi->u.s.NumberOfProcessors, str,
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
                   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;
323
            default: str = "4-????"; break;
324 325 326 327 328 329 330
            }
            break;
        case 5:
            switch (msi->MinorVersion)
            {
            case 0: str = "2000"; break;
            case 1: str = "XP"; break;
331 332 333 334 335
            case 2:
                if (msi->u.s.ProductType == 1) str = "XP";
                else if (msi->u.s.ProductType == 3) str = "Server 2003";
                else str = "5-????";
                break;
336 337 338
            default: str = "5-????"; break;
            }
            break;
339 340 341 342 343 344 345 346 347 348 349 350
        case 6:
            switch (msi->MinorVersion)
            {
            case 0:
                if (msi->u.s.ProductType == 1) str = "Vista";
                else if (msi->u.s.ProductType == 3) str = "Server 2008";
                else str = "6-????";
                break;
            case 1:
                if (msi->u.s.ProductType == 1) str = "Win7";
                else if (msi->u.s.ProductType == 3) str = "Server 2008";
                else str = "6-????";
351
                break;
352 353 354 355 356 357 358 359 360 361
            case 2:
                if (msi->u.s.ProductType == 1) str = "Win8";
                else if (msi->u.s.ProductType == 3) str = "Server 2012";
                else str = "6-????";
                break;
            case 3:
                if (msi->u.s.ProductType == 1) str = "Win8.1";
                else if (msi->u.s.ProductType == 3) str = "Server 2012 R2";
                else str = "6-????";
                break;
362 363 364
            default: str = "6-????"; break;
            }
            break;
365 366 367 368 369 370 371 372 373 374
        case 10:
            switch (msi->MinorVersion)
            {
            case 0:
                if (msi->u.s.ProductType == 1) str = "Win10";
                else str = "10-????";
                break;
            default: str = "10-????"; break;
            }
            break;
375 376
        default: str = "???"; break;
        }
377
        dbg_printf(" on Windows %s (%u)\n", str, msi->BuildNumber);
378
        /* FIXME CSD: msi->CSDVersionRva */
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

        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]);
            }
        }
394 395 396 397 398 399 400
    }

    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);

401
    dbg_init(hProc, NULL, FALSE);
402

403
    if (MiniDumpReadDumpStream(data->mapping, ThreadListStream, NULL, &stream, NULL))
404
    {
405
        MINIDUMP_THREAD_LIST*   mtl = stream;
406
        ULONG                   i;
407

408 409 410
        for (i = 0; i < mtl->NumberOfThreads; i++)
        {
            dbg_add_thread(dbg_curr_process, mtl->Threads[i].ThreadId, NULL,
411
                           (void*)(DWORD_PTR)get_addr64(mtl->Threads[i].Teb));
412
        }
413 414
    }
    /* first load ELF modules, then do the PE ones */
415 416
    if (MiniDumpReadDumpStream(data->mapping, Wine_ElfModuleListStream, NULL,
                               &stream, NULL))
417
    {
418
        WCHAR   buffer[MAX_PATH];
419

420
        mml = stream;
421
        for (i = 0, mm = mml->Modules; i < mml->NumberOfModules; i++, mm++)
422 423
        {
            mds = (MINIDUMP_STRING*)((char*)data->mapping + mm->ModuleNameRva);
424 425 426 427
            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))
428 429
                dbg_load_module(hProc, NULL, buffer, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
430
            else
431 432
                SymLoadModuleExW(hProc, NULL, nameW, NULL, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage, NULL, SLMFLAG_VIRTUAL);
433 434
        }
    }
435
    if (MiniDumpReadDumpStream(data->mapping, ModuleListStream, NULL, &stream, NULL))
436
    {
437 438
        WCHAR   buffer[MAX_PATH];

439
        mml = stream;
440
        for (i = 0, mm = mml->Modules; i < mml->NumberOfModules; i++, mm++)
441 442
        {
            mds = (MINIDUMP_STRING*)((char*)data->mapping + mm->ModuleNameRva);
443 444
            memcpy(nameW, mds->Buffer, mds->Length);
            nameW[mds->Length / sizeof(WCHAR)] = 0;
445 446
            if (SymFindFileInPathW(hProc, NULL, nameW, (void*)(DWORD_PTR)mm->TimeDateStamp,
                                   mm->SizeOfImage, 0, SSRVOPT_DWORD, buffer, validate_file, NULL))
447 448
                dbg_load_module(hProc, NULL, buffer, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
449
            else if (is_pe_module_embedded(data, mm))
450 451
                dbg_load_module(hProc, NULL, nameW, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage);
452
            else
453 454
                SymLoadModuleExW(hProc, NULL, nameW, NULL, get_addr64(mm->BaseOfImage),
                                 mm->SizeOfImage, NULL, SLMFLAG_VIRTUAL);
455 456
        }
    }
457
    if (MiniDumpReadDumpStream(data->mapping, ExceptionStream, NULL, &stream, NULL))
458
    {
459
        MINIDUMP_EXCEPTION_STREAM*      mes = stream;
460 461 462

        if ((dbg_curr_thread = dbg_get_thread(dbg_curr_process, mes->ThreadId)))
        {
463
            ADDRESS64   addr;
464 465 466 467 468

            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;
469 470
            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);
471 472 473 474 475 476 477 478
            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);
479
            stack_fetch_frames(&dbg_context);
480
            be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0);
481
            stack_info(-1);
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
            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)
{
541
    struct tgt_process_minidump_data*    data = private_data(pcs);
542 543 544 545 546 547 548 549

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

550
static BOOL tgt_process_minidump_get_selector(HANDLE hThread, DWORD sel, LDT_ENTRY* le)
551 552 553 554 555 556 557
{
    /* 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;
}

558 559 560 561 562
static struct be_process_io be_process_minidump_io =
{
    tgt_process_minidump_close_process,
    tgt_process_minidump_read,
    tgt_process_minidump_write,
563
    tgt_process_minidump_get_selector,
564
};