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/asm.h" 32 #include "wine/debug.h" 33 34 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); 35 36 /****************************************************************** 37 * fetch_process_info 38 * 39 * reads system wide process information, and gather from it the threads information 40 * for process of id 'pid' 41 */ 42 static BOOL fetch_process_info(struct dump_context* dc) 43 { 44 ULONG buf_size = 0x1000; 45 NTSTATUS nts; 46 void* pcs_buffer = NULL; 47 48 if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE; 49 for (;;) 50 { 51 nts = NtQuerySystemInformation(SystemProcessInformation, 52 pcs_buffer, buf_size, NULL); 53 if (nts != STATUS_INFO_LENGTH_MISMATCH) break; 54 pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2); 55 if (!pcs_buffer) return FALSE; 56 } 57 58 if (nts == STATUS_SUCCESS) 59 { 60 SYSTEM_PROCESS_INFORMATION* spi = pcs_buffer; 61 unsigned i; 62 63 for (;;) 64 { 65 if (HandleToUlong(spi->UniqueProcessId) == dc->pid) 66 { 67 dc->num_threads = spi->dwThreadCount; 68 dc->threads = HeapAlloc(GetProcessHeap(), 0, 69 dc->num_threads * sizeof(dc->threads[0])); 70 if (!dc->threads) goto failed; 71 for (i = 0; i < dc->num_threads; i++) 72 { 73 dc->threads[i].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread); 74 dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */ 75 dc->threads[i].curr_prio = spi->ti[i].dwCurrentPriority; 76 } 77 HeapFree(GetProcessHeap(), 0, pcs_buffer); 78 return TRUE; 79 } 80 if (!spi->NextEntryOffset) break; 81 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset); 82 } 83 } 84 failed: 85 HeapFree(GetProcessHeap(), 0, pcs_buffer); 86 return FALSE; 87 } 88 89 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr, 90 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd) 91 { 92 NT_TIB tib; 93 ADDRESS64 addr; 94 95 if (ReadProcessMemory(dc->process->handle, teb_addr, &tib, sizeof(tib), NULL) && 96 dbghelp_current_cpu && 97 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat) 98 { 99 if (addr.Offset) 100 { 101 addr.Offset -= dbghelp_current_cpu->word_size; 102 /* make sure stack pointer is within the established range of the stack. It could have 103 been clobbered by whatever caused the original exception. */ 104 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase) 105 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 106 107 else 108 mmd->StartOfMemoryRange = addr.Offset; 109 } 110 else 111 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 112 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange; 113 } 114 } 115 116 /****************************************************************** 117 * fetch_thread_info 118 * 119 * fetches some information about thread of id 'tid' 120 */ 121 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx, 122 const MINIDUMP_EXCEPTION_INFORMATION* except, 123 MINIDUMP_THREAD* mdThd, CONTEXT* ctx) 124 { 125 DWORD tid = dc->threads[thd_idx].tid; 126 HANDLE hThread; 127 THREAD_BASIC_INFORMATION tbi; 128 129 memset(ctx, 0, sizeof(*ctx)); 130 131 mdThd->ThreadId = tid; 132 mdThd->SuspendCount = 0; 133 mdThd->Teb = 0; 134 mdThd->Stack.StartOfMemoryRange = 0; 135 mdThd->Stack.Memory.DataSize = 0; 136 mdThd->Stack.Memory.Rva = 0; 137 mdThd->ThreadContext.DataSize = 0; 138 mdThd->ThreadContext.Rva = 0; 139 mdThd->PriorityClass = dc->threads[thd_idx].prio_class; 140 mdThd->Priority = dc->threads[thd_idx].curr_prio; 141 142 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL) 143 { 144 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError()); 145 return FALSE; 146 } 147 148 if (NtQueryInformationThread(hThread, ThreadBasicInformation, 149 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS) 150 { 151 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress; 152 if (tbi.ExitStatus == STILL_ACTIVE) 153 { 154 if (tid != GetCurrentThreadId() && 155 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1) 156 { 157 ctx->ContextFlags = CONTEXT_FULL; 158 if (!GetThreadContext(hThread, ctx)) 159 memset(ctx, 0, sizeof(*ctx)); 160 161 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack); 162 ResumeThread(hThread); 163 } 164 else if (tid == GetCurrentThreadId() && except) 165 { 166 CONTEXT lctx, *pctx; 167 mdThd->SuspendCount = 1; 168 if (except->ClientPointers) 169 { 170 EXCEPTION_POINTERS ep; 171 172 ReadProcessMemory(dc->process->handle, except->ExceptionPointers, 173 &ep, sizeof(ep), NULL); 174 ReadProcessMemory(dc->process->handle, ep.ContextRecord, 175 &lctx, sizeof(lctx), NULL); 176 pctx = &lctx; 177 } 178 else pctx = except->ExceptionPointers->ContextRecord; 179 180 *ctx = *pctx; 181 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack); 182 } 183 else mdThd->SuspendCount = 0; 184 } 185 } 186 CloseHandle(hThread); 187 return TRUE; 188 } 189 190 /****************************************************************** 191 * add_module 192 * 193 * Add a module to a dump context 194 */ 195 static BOOL add_module(struct dump_context* dc, const WCHAR* name, 196 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum, 197 BOOL is_elf) 198 { 199 if (!dc->modules) 200 { 201 dc->alloc_modules = 32; 202 dc->modules = HeapAlloc(GetProcessHeap(), 0, 203 dc->alloc_modules * sizeof(*dc->modules)); 204 } 205 else if(dc->num_modules >= dc->alloc_modules) 206 { 207 dc->alloc_modules *= 2; 208 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules, 209 dc->alloc_modules * sizeof(*dc->modules)); 210 } 211 if (!dc->modules) 212 { 213 dc->alloc_modules = dc->num_modules = 0; 214 return FALSE; 215 } 216 if (is_elf || 217 !GetModuleFileNameExW(dc->process->handle, (HMODULE)(DWORD_PTR)base, 218 dc->modules[dc->num_modules].name, 219 ARRAY_SIZE(dc->modules[dc->num_modules].name))) 220 lstrcpynW(dc->modules[dc->num_modules].name, name, 221 ARRAY_SIZE(dc->modules[dc->num_modules].name)); 222 dc->modules[dc->num_modules].base = base; 223 dc->modules[dc->num_modules].size = size; 224 dc->modules[dc->num_modules].timestamp = timestamp; 225 dc->modules[dc->num_modules].checksum = checksum; 226 dc->modules[dc->num_modules].is_elf = is_elf; 227 dc->num_modules++; 228 229 return TRUE; 230 } 231 232 /****************************************************************** 233 * fetch_pe_module_info_cb 234 * 235 * Callback for accumulating in dump_context a PE modules set 236 */ 237 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size, 238 PVOID user) 239 { 240 struct dump_context* dc = user; 241 IMAGE_NT_HEADERS nth; 242 243 if (!validate_addr64(base)) return FALSE; 244 245 if (pe_load_nt_header(dc->process->handle, base, &nth)) 246 add_module(user, name, base, size, 247 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum, 248 FALSE); 249 return TRUE; 250 } 251 252 /****************************************************************** 253 * fetch_elf_module_info_cb 254 * 255 * Callback for accumulating in dump_context an host modules set 256 */ 257 static BOOL fetch_host_module_info_cb(const WCHAR* name, ULONG_PTR base, 258 void* user) 259 { 260 struct dump_context* dc = user; 261 DWORD_PTR rbase; 262 DWORD size, checksum; 263 264 /* FIXME: there's no relevant timestamp on ELF modules */ 265 if (!dc->process->loader->fetch_file_info(dc->process, name, base, &rbase, &size, &checksum)) 266 size = checksum = 0; 267 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE); 268 return TRUE; 269 } 270 271 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size) 272 { 273 if (!dc->mem64) 274 { 275 dc->alloc_mem64 = 32; 276 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64)); 277 } 278 else if (dc->num_mem64 >= dc->alloc_mem64) 279 { 280 dc->alloc_mem64 *= 2; 281 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64, 282 dc->alloc_mem64 * sizeof(*dc->mem64)); 283 } 284 if (dc->mem64) 285 { 286 dc->mem64[dc->num_mem64].base = base; 287 dc->mem64[dc->num_mem64].size = size; 288 dc->num_mem64++; 289 } 290 else dc->num_mem64 = dc->alloc_mem64 = 0; 291 } 292 293 static void fetch_memory64_info(struct dump_context* dc) 294 { 295 ULONG_PTR addr; 296 MEMORY_BASIC_INFORMATION mbi; 297 298 addr = 0; 299 while (VirtualQueryEx(dc->process->handle, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0) 300 { 301 /* Memory regions with state MEM_COMMIT will be added to the dump */ 302 if (mbi.State == MEM_COMMIT) 303 { 304 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize); 305 } 306 307 if ((addr + mbi.RegionSize) < addr) 308 break; 309 310 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; 311 } 312 } 313 314 static void fetch_modules_info(struct dump_context* dc) 315 { 316 EnumerateLoadedModulesW64(dc->process->handle, fetch_pe_module_info_cb, dc); 317 /* Since we include ELF modules in a separate stream from the regular PE ones, 318 * we can always include those ELF modules (they don't eat lots of space) 319 * And it's always a good idea to have a trace of the loaded ELF modules for 320 * a given application in a post mortem debugging condition. 321 */ 322 dc->process->loader->enum_modules(dc->process, fetch_host_module_info_cb, dc); 323 } 324 325 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi) 326 { 327 DWORD handle; 328 DWORD sz; 329 static const WCHAR backslashW[] = {'\\', '\0'}; 330 331 memset(ffi, 0, sizeof(*ffi)); 332 if ((sz = GetFileVersionInfoSizeW(filename, &handle))) 333 { 334 void* info = HeapAlloc(GetProcessHeap(), 0, sz); 335 if (info && GetFileVersionInfoW(filename, handle, sz, info)) 336 { 337 VS_FIXEDFILEINFO* ptr; 338 UINT len; 339 340 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len)) 341 memcpy(ffi, ptr, min(len, sizeof(*ffi))); 342 } 343 HeapFree(GetProcessHeap(), 0, info); 344 } 345 } 346 347 /****************************************************************** 348 * minidump_add_memory_block 349 * 350 * Add a memory block to be dumped in a minidump 351 * If rva is non 0, it's the rva in the minidump where has to be stored 352 * also the rva of the memory block when written (this allows us to reference 353 * a memory block from outside the list of memory blocks). 354 */ 355 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva) 356 { 357 if (!dc->mem) 358 { 359 dc->alloc_mem = 32; 360 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem)); 361 } 362 else if (dc->num_mem >= dc->alloc_mem) 363 { 364 dc->alloc_mem *= 2; 365 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 366 dc->alloc_mem * sizeof(*dc->mem)); 367 } 368 if (dc->mem) 369 { 370 dc->mem[dc->num_mem].base = base; 371 dc->mem[dc->num_mem].size = size; 372 dc->mem[dc->num_mem].rva = rva; 373 dc->num_mem++; 374 } 375 else dc->num_mem = dc->alloc_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, const 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 DWORD i; 415 416 mdExcpt.ThreadId = except->ThreadId; 417 mdExcpt.__alignment = 0; 418 if (except->ClientPointers) 419 { 420 EXCEPTION_POINTERS ep; 421 422 ReadProcessMemory(dc->process->handle, 423 except->ExceptionPointers, &ep, sizeof(ep), NULL); 424 ReadProcessMemory(dc->process->handle, 425 ep.ExceptionRecord, &rec, sizeof(rec), NULL); 426 ReadProcessMemory(dc->process->handle, 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->process->handle; 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 /* fetch CPU dependent module info (like UNWIND_INFO) */ 534 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out); 535 536 mdModule.BaseOfImage = dc->modules[i].base; 537 mdModule.SizeOfImage = dc->modules[i].size; 538 mdModule.CheckSum = dc->modules[i].checksum; 539 mdModule.TimeDateStamp = dc->modules[i].timestamp; 540 mdModule.ModuleNameRva = dc->rva; 541 ms->Length -= sizeof(WCHAR); 542 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR)); 543 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo); 544 mdModule.CvRecord.DataSize = 0; /* FIXME */ 545 mdModule.CvRecord.Rva = 0; /* FIXME */ 546 mdModule.MiscRecord.DataSize = 0; /* FIXME */ 547 mdModule.MiscRecord.Rva = 0; /* FIXME */ 548 mdModule.Reserved0 = 0; /* FIXME */ 549 mdModule.Reserved1 = 0; /* FIXME */ 550 writeat(dc, 551 rva_base + sizeof(mdModuleList.NumberOfModules) + 552 mdModuleList.NumberOfModules++ * sizeof(mdModule), 553 &mdModule, sizeof(mdModule)); 554 } 555 } 556 writeat(dc, rva_base, &mdModuleList.NumberOfModules, 557 sizeof(mdModuleList.NumberOfModules)); 558 559 return sz; 560 } 561 562 #ifdef __i386__ 563 #ifndef __REACTOS__ 564 extern void do_x86cpuid(unsigned int ax, unsigned int *p); 565 __ASM_GLOBAL_FUNC( do_x86cpuid, 566 "pushl %esi\n\t" 567 "pushl %ebx\n\t" 568 "movl 12(%esp),%eax\n\t" 569 "movl 16(%esp),%esi\n\t" 570 "cpuid\n\t" 571 "movl %eax,(%esi)\n\t" 572 "movl %ebx,4(%esi)\n\t" 573 "movl %ecx,8(%esi)\n\t" 574 "movl %edx,12(%esi)\n\t" 575 "popl %ebx\n\t" 576 "popl %esi\n\t" 577 "ret" ) 578 extern int have_x86cpuid(void); 579 __ASM_GLOBAL_FUNC( have_x86cpuid, 580 "pushfl\n\t" 581 "pushfl\n\t" 582 "movl (%esp),%ecx\n\t" 583 "xorl $0x00200000,(%esp)\n\t" 584 "popfl\n\t" 585 "pushfl\n\t" 586 "popl %eax\n\t" 587 "popfl\n\t" 588 "xorl %ecx,%eax\n\t" 589 "andl $0x00200000,%eax\n\t" 590 "ret" ) 591 #else 592 static void do_x86cpuid(unsigned int ax, unsigned int *p) 593 { 594 __cpuid((int*)p, ax); 595 } 596 597 static int have_x86cpuid(void) 598 { 599 ULONG CallerFlags, Flags; 600 601 /* Read the original flags, and add the CPUID bit */ 602 CallerFlags = __readeflags() ^ 0x200000; 603 __writeeflags(CallerFlags); 604 605 /* Read our flags now */ 606 Flags = __readeflags(); 607 608 /* Check if the bit stuck */ 609 return (((CallerFlags ^ Flags) >> 21) & 1) ^ 1; 610 } 611 #endif 612 #else 613 static void do_x86cpuid(unsigned int ax, unsigned int *p) 614 { 615 } 616 617 static int have_x86cpuid(void) 618 { 619 return 0; 620 } 621 #endif 622 623 /****************************************************************** 624 * dump_system_info 625 * 626 * Dumps into File the information about the system 627 */ 628 static unsigned dump_system_info(struct dump_context* dc) 629 { 630 MINIDUMP_SYSTEM_INFO mdSysInfo; 631 SYSTEM_INFO sysInfo; 632 OSVERSIONINFOW osInfo; 633 DWORD written; 634 ULONG slen; 635 DWORD wine_extra = 0; 636 637 const char *(CDECL *wine_get_build_id)(void); 638 void (CDECL *wine_get_host_version)(const char **sysname, const char **release); 639 const char* build_id = NULL; 640 const char* sys_name = NULL; 641 const char* release_name = NULL; 642 643 GetSystemInfo(&sysInfo); 644 osInfo.dwOSVersionInfoSize = sizeof(osInfo); 645 GetVersionExW(&osInfo); 646 647 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id"); 648 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version"); 649 if (wine_get_build_id && wine_get_host_version) 650 { 651 /* cheat minidump system information by adding specific wine information */ 652 wine_extra = 4 + 4 * sizeof(slen); 653 build_id = wine_get_build_id(); 654 wine_get_host_version(&sys_name, &release_name); 655 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1; 656 } 657 658 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture; 659 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel; 660 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision; 661 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors; 662 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */ 663 mdSysInfo.MajorVersion = osInfo.dwMajorVersion; 664 mdSysInfo.MinorVersion = osInfo.dwMinorVersion; 665 mdSysInfo.BuildNumber = osInfo.dwBuildNumber; 666 mdSysInfo.PlatformId = osInfo.dwPlatformId; 667 668 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra; 669 mdSysInfo.u1.Reserved1 = 0; 670 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL; 671 672 if (have_x86cpuid()) 673 { 674 unsigned regs0[4], regs1[4]; 675 676 do_x86cpuid(0, regs0); 677 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1]; 678 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3]; 679 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2]; 680 do_x86cpuid(1, regs1); 681 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0]; 682 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3]; 683 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0; 684 if (regs0[1] == 0x68747541 /* "Auth" */ && 685 regs0[3] == 0x69746e65 /* "enti" */ && 686 regs0[2] == 0x444d4163 /* "cAMD" */) 687 { 688 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */ 689 if (regs1[0] >= 0x80000001) 690 { 691 do_x86cpuid(0x80000001, regs1); /* get vendor features */ 692 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3]; 693 } 694 } 695 } 696 else 697 { 698 unsigned i; 699 ULONG64 one = 1; 700 701 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0; 702 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; 703 704 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++) 705 if (IsProcessorFeaturePresent(i)) 706 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i; 707 } 708 append(dc, &mdSysInfo, sizeof(mdSysInfo)); 709 710 /* write Wine specific system information just behind the structure, and before any string */ 711 if (wine_extra) 712 { 713 static const char code[] = {'W','I','N','E'}; 714 715 WriteFile(dc->hFile, code, 4, &written, NULL); 716 /* number of sub-info, so that we can extend structure if needed */ 717 slen = 3; 718 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 719 /* we store offsets from just after the WINE marker */ 720 slen = 4 * sizeof(DWORD); 721 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 722 slen += strlen(build_id) + 1; 723 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 724 slen += strlen(sys_name) + 1; 725 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 726 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL); 727 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL); 728 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL); 729 dc->rva += wine_extra; 730 } 731 732 /* write the service pack version string after this stream. It is referenced within the 733 stream by its RVA in the file. */ 734 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR); 735 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 736 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL); 737 dc->rva += sizeof(ULONG) + slen; 738 739 return sizeof(mdSysInfo); 740 } 741 742 /****************************************************************** 743 * dump_threads 744 * 745 * Dumps into File the information about running threads 746 */ 747 static unsigned dump_threads(struct dump_context* dc, 748 const MINIDUMP_EXCEPTION_INFORMATION* except) 749 { 750 MINIDUMP_THREAD mdThd; 751 MINIDUMP_THREAD_LIST mdThdList; 752 unsigned i, sz; 753 RVA rva_base; 754 DWORD flags_out; 755 CONTEXT ctx; 756 757 mdThdList.NumberOfThreads = 0; 758 759 rva_base = dc->rva; 760 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd); 761 762 for (i = 0; i < dc->num_threads; i++) 763 { 764 fetch_thread_info(dc, i, except, &mdThd, &ctx); 765 766 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | 767 ThreadWriteInstructionWindow; 768 if (dc->type & MiniDumpWithProcessThreadData) 769 flags_out |= ThreadWriteThreadData; 770 if (dc->type & MiniDumpWithThreadInfo) 771 flags_out |= ThreadWriteThreadInfo; 772 773 if (dc->cb) 774 { 775 MINIDUMP_CALLBACK_INPUT cbin; 776 MINIDUMP_CALLBACK_OUTPUT cbout; 777 778 cbin.ProcessId = dc->pid; 779 cbin.ProcessHandle = dc->process->handle; 780 cbin.CallbackType = ThreadCallback; 781 cbin.u.Thread.ThreadId = dc->threads[i].tid; 782 cbin.u.Thread.ThreadHandle = 0; /* FIXME */ 783 cbin.u.Thread.Context = ctx; 784 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT); 785 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange; 786 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange + 787 mdThd.Stack.Memory.DataSize; 788 789 cbout.u.ThreadWriteFlags = flags_out; 790 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) 791 continue; 792 flags_out &= cbout.u.ThreadWriteFlags; 793 } 794 if (flags_out & ThreadWriteThread) 795 { 796 if (ctx.ContextFlags && (flags_out & ThreadWriteContext)) 797 { 798 mdThd.ThreadContext.Rva = dc->rva; 799 mdThd.ThreadContext.DataSize = sizeof(CONTEXT); 800 append(dc, &ctx, sizeof(CONTEXT)); 801 } 802 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack)) 803 { 804 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange, 805 mdThd.Stack.Memory.DataSize, 806 rva_base + sizeof(mdThdList.NumberOfThreads) + 807 mdThdList.NumberOfThreads * sizeof(mdThd) + 808 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva)); 809 } 810 writeat(dc, 811 rva_base + sizeof(mdThdList.NumberOfThreads) + 812 mdThdList.NumberOfThreads * sizeof(mdThd), 813 &mdThd, sizeof(mdThd)); 814 mdThdList.NumberOfThreads++; 815 } 816 /* fetch CPU dependent thread info (like 256 bytes around program counter */ 817 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx); 818 } 819 writeat(dc, rva_base, 820 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads)); 821 822 return sz; 823 } 824 825 /****************************************************************** 826 * dump_memory_info 827 * 828 * dumps information about the memory of the process (stack of the threads) 829 */ 830 static unsigned dump_memory_info(struct dump_context* dc) 831 { 832 MINIDUMP_MEMORY_LIST mdMemList; 833 MINIDUMP_MEMORY_DESCRIPTOR mdMem; 834 DWORD written; 835 unsigned i, pos, len, sz; 836 RVA rva_base; 837 char tmp[1024]; 838 839 mdMemList.NumberOfMemoryRanges = dc->num_mem; 840 append(dc, &mdMemList.NumberOfMemoryRanges, 841 sizeof(mdMemList.NumberOfMemoryRanges)); 842 rva_base = dc->rva; 843 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem); 844 dc->rva += sz; 845 sz += sizeof(mdMemList.NumberOfMemoryRanges); 846 847 for (i = 0; i < dc->num_mem; i++) 848 { 849 mdMem.StartOfMemoryRange = dc->mem[i].base; 850 mdMem.Memory.Rva = dc->rva; 851 mdMem.Memory.DataSize = dc->mem[i].size; 852 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN); 853 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp)) 854 { 855 len = min(dc->mem[i].size - pos, sizeof(tmp)); 856 if (read_process_memory(dc->process, dc->mem[i].base + pos, tmp, len)) 857 WriteFile(dc->hFile, tmp, len, &written, NULL); 858 } 859 dc->rva += mdMem.Memory.DataSize; 860 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem)); 861 if (dc->mem[i].rva) 862 { 863 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva)); 864 } 865 } 866 867 return sz; 868 } 869 870 /****************************************************************** 871 * dump_memory64_info 872 * 873 * dumps information about the memory of the process (virtual memory) 874 */ 875 static unsigned dump_memory64_info(struct dump_context* dc) 876 { 877 MINIDUMP_MEMORY64_LIST mdMem64List; 878 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64; 879 DWORD written; 880 unsigned i, len, sz; 881 RVA rva_base; 882 char tmp[1024]; 883 ULONG64 pos; 884 LARGE_INTEGER filepos; 885 886 sz = sizeof(mdMem64List.NumberOfMemoryRanges) + 887 sizeof(mdMem64List.BaseRva) + 888 dc->num_mem64 * sizeof(mdMem64); 889 890 mdMem64List.NumberOfMemoryRanges = dc->num_mem64; 891 mdMem64List.BaseRva = dc->rva + sz; 892 893 append(dc, &mdMem64List.NumberOfMemoryRanges, 894 sizeof(mdMem64List.NumberOfMemoryRanges)); 895 append(dc, &mdMem64List.BaseRva, 896 sizeof(mdMem64List.BaseRva)); 897 898 rva_base = dc->rva; 899 dc->rva += dc->num_mem64 * sizeof(mdMem64); 900 901 /* dc->rva is not updated past this point. The end of the dump 902 * is just the full memory data. */ 903 filepos.QuadPart = dc->rva; 904 for (i = 0; i < dc->num_mem64; i++) 905 { 906 mdMem64.StartOfMemoryRange = dc->mem64[i].base; 907 mdMem64.DataSize = dc->mem64[i].size; 908 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN); 909 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp)) 910 { 911 len = min(dc->mem64[i].size - pos, sizeof(tmp)); 912 if (read_process_memory(dc->process, dc->mem64[i].base + pos, tmp, len)) 913 WriteFile(dc->hFile, tmp, len, &written, NULL); 914 } 915 filepos.QuadPart += mdMem64.DataSize; 916 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64)); 917 } 918 919 return sz; 920 } 921 922 static unsigned dump_misc_info(struct dump_context* dc) 923 { 924 MINIDUMP_MISC_INFO mmi; 925 926 mmi.SizeOfInfo = sizeof(mmi); 927 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID; 928 mmi.ProcessId = dc->pid; 929 /* FIXME: create/user/kernel time */ 930 mmi.ProcessCreateTime = 0; 931 mmi.ProcessKernelTime = 0; 932 mmi.ProcessUserTime = 0; 933 934 append(dc, &mmi, sizeof(mmi)); 935 return sizeof(mmi); 936 } 937 938 /****************************************************************** 939 * MiniDumpWriteDump (DEBUGHLP.@) 940 * 941 */ 942 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, 943 MINIDUMP_TYPE DumpType, 944 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 945 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 946 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) 947 { 948 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}}; 949 MINIDUMP_HEADER mdHead; 950 MINIDUMP_DIRECTORY mdDir; 951 DWORD i, nStreams, idx_stream; 952 struct dump_context dc; 953 BOOL sym_initialized = FALSE; 954 955 if (!(dc.process = process_find_by_handle(hProcess))) 956 { 957 if (!(sym_initialized = SymInitializeW(hProcess, NULL, TRUE))) 958 { 959 WARN("failed to initialize process\n"); 960 return FALSE; 961 } 962 dc.process = process_find_by_handle(hProcess); 963 } 964 965 dc.hFile = hFile; 966 dc.pid = pid; 967 dc.modules = NULL; 968 dc.num_modules = 0; 969 dc.alloc_modules = 0; 970 dc.threads = NULL; 971 dc.num_threads = 0; 972 dc.cb = CallbackParam; 973 dc.type = DumpType; 974 dc.mem = NULL; 975 dc.num_mem = 0; 976 dc.alloc_mem = 0; 977 dc.mem64 = NULL; 978 dc.num_mem64 = 0; 979 dc.alloc_mem64 = 0; 980 dc.rva = 0; 981 982 if (!fetch_process_info(&dc)) return FALSE; 983 fetch_modules_info(&dc); 984 985 /* 1) init */ 986 nStreams = 6 + (ExceptionParam ? 1 : 0) + 987 (UserStreamParam ? UserStreamParam->UserStreamCount : 0); 988 989 /* pad the directory size to a multiple of 4 for alignment purposes */ 990 nStreams = (nStreams + 3) & ~3; 991 992 if (DumpType & MiniDumpWithDataSegs) 993 FIXME("NIY MiniDumpWithDataSegs\n"); 994 if (DumpType & MiniDumpWithHandleData) 995 FIXME("NIY MiniDumpWithHandleData\n"); 996 if (DumpType & MiniDumpFilterMemory) 997 FIXME("NIY MiniDumpFilterMemory\n"); 998 if (DumpType & MiniDumpScanMemory) 999 FIXME("NIY MiniDumpScanMemory\n"); 1000 1001 /* 2) write header */ 1002 mdHead.Signature = MINIDUMP_SIGNATURE; 1003 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */ 1004 mdHead.NumberOfStreams = nStreams; 1005 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */ 1006 mdHead.StreamDirectoryRva = sizeof(mdHead); 1007 mdHead.u.TimeDateStamp = time(NULL); 1008 mdHead.Flags = DumpType; 1009 append(&dc, &mdHead, sizeof(mdHead)); 1010 1011 /* 3) write stream directories */ 1012 dc.rva += nStreams * sizeof(mdDir); 1013 idx_stream = 0; 1014 1015 /* 3.1) write data stream directories */ 1016 1017 /* must be first in minidump */ 1018 mdDir.StreamType = SystemInfoStream; 1019 mdDir.Location.Rva = dc.rva; 1020 mdDir.Location.DataSize = dump_system_info(&dc); 1021 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1022 &mdDir, sizeof(mdDir)); 1023 1024 mdDir.StreamType = ThreadListStream; 1025 mdDir.Location.Rva = dc.rva; 1026 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam); 1027 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1028 &mdDir, sizeof(mdDir)); 1029 1030 mdDir.StreamType = ModuleListStream; 1031 mdDir.Location.Rva = dc.rva; 1032 mdDir.Location.DataSize = dump_modules(&dc, FALSE); 1033 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1034 &mdDir, sizeof(mdDir)); 1035 1036 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */ 1037 mdDir.Location.Rva = dc.rva; 1038 mdDir.Location.DataSize = dump_modules(&dc, TRUE); 1039 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1040 &mdDir, sizeof(mdDir)); 1041 1042 1043 if (!(DumpType & MiniDumpWithFullMemory)) 1044 { 1045 mdDir.StreamType = MemoryListStream; 1046 mdDir.Location.Rva = dc.rva; 1047 mdDir.Location.DataSize = dump_memory_info(&dc); 1048 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1049 &mdDir, sizeof(mdDir)); 1050 } 1051 1052 mdDir.StreamType = MiscInfoStream; 1053 mdDir.Location.Rva = dc.rva; 1054 mdDir.Location.DataSize = dump_misc_info(&dc); 1055 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1056 &mdDir, sizeof(mdDir)); 1057 1058 /* 3.2) write exception information (if any) */ 1059 if (ExceptionParam) 1060 { 1061 mdDir.StreamType = ExceptionStream; 1062 mdDir.Location.Rva = dc.rva; 1063 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam); 1064 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1065 &mdDir, sizeof(mdDir)); 1066 } 1067 1068 /* 3.3) write user defined streams (if any) */ 1069 if (UserStreamParam) 1070 { 1071 for (i = 0; i < UserStreamParam->UserStreamCount; i++) 1072 { 1073 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type; 1074 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize; 1075 mdDir.Location.Rva = dc.rva; 1076 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1077 &mdDir, sizeof(mdDir)); 1078 append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 1079 UserStreamParam->UserStreamArray[i].BufferSize); 1080 } 1081 } 1082 1083 /* 3.4) write full memory (if requested) */ 1084 if (DumpType & MiniDumpWithFullMemory) 1085 { 1086 fetch_memory64_info(&dc); 1087 1088 mdDir.StreamType = Memory64ListStream; 1089 mdDir.Location.Rva = dc.rva; 1090 mdDir.Location.DataSize = dump_memory64_info(&dc); 1091 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 1092 &mdDir, sizeof(mdDir)); 1093 } 1094 1095 /* fill the remaining directory entries with 0's (unused stream types) */ 1096 /* NOTE: this should always come last in the dump! */ 1097 for (i = idx_stream; i < nStreams; i++) 1098 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir)); 1099 1100 if (sym_initialized) 1101 SymCleanup(hProcess); 1102 1103 HeapFree(GetProcessHeap(), 0, dc.mem); 1104 HeapFree(GetProcessHeap(), 0, dc.mem64); 1105 HeapFree(GetProcessHeap(), 0, dc.modules); 1106 HeapFree(GetProcessHeap(), 0, dc.threads); 1107 1108 return TRUE; 1109 } 1110 1111 /****************************************************************** 1112 * MiniDumpReadDumpStream (DEBUGHLP.@) 1113 * 1114 * 1115 */ 1116 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx, 1117 PMINIDUMP_DIRECTORY* pdir, 1118 PVOID* stream, ULONG* size) 1119 { 1120 MINIDUMP_HEADER* mdHead = base; 1121 1122 if (mdHead->Signature == MINIDUMP_SIGNATURE) 1123 { 1124 MINIDUMP_DIRECTORY* dir; 1125 DWORD i; 1126 1127 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva); 1128 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++) 1129 { 1130 if (dir->StreamType == str_idx) 1131 { 1132 if (pdir) *pdir = dir; 1133 if (stream) *stream = (char*)base + dir->Location.Rva; 1134 if (size) *size = dir->Location.DataSize; 1135 return TRUE; 1136 } 1137 } 1138 } 1139 SetLastError(ERROR_INVALID_PARAMETER); 1140 return FALSE; 1141 } 1142