1 /* 2 * File minidump.c - management of dumps (read & write) 3 * 4 * Copyright (C) 2004-2005, Eric Pouech 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <time.h> 22 23 #define NONAMELESSUNION 24 #define NONAMELESSSTRUCT 25 26 #include "ntstatus.h" 27 #define WIN32_NO_STATUS 28 #include "dbghelp_private.h" 29 #include "winternl.h" 30 #include "psapi.h" 31 #include "wine/debug.h" 32 33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); 34 35 /****************************************************************** 36 * fetch_process_info 37 * 38 * reads system wide process information, and gather from it the threads information 39 * for process of id 'pid' 40 */ 41 static BOOL fetch_process_info(struct dump_context* dc) 42 { 43 ULONG buf_size = 0x1000; 44 NTSTATUS nts; 45 void* pcs_buffer = NULL; 46 47 if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE; 48 for (;;) 49 { 50 nts = NtQuerySystemInformation(SystemProcessInformation, 51 pcs_buffer, buf_size, NULL); 52 if (nts != STATUS_INFO_LENGTH_MISMATCH) break; 53 pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2); 54 if (!pcs_buffer) return FALSE; 55 } 56 57 if (nts == STATUS_SUCCESS) 58 { 59 SYSTEM_PROCESS_INFORMATION* spi = pcs_buffer; 60 unsigned i; 61 62 for (;;) 63 { 64 if (HandleToUlong(spi->UniqueProcessId) == dc->pid) 65 { 66 dc->num_threads = spi->dwThreadCount; 67 dc->threads = HeapAlloc(GetProcessHeap(), 0, 68 dc->num_threads * sizeof(dc->threads[0])); 69 if (!dc->threads) goto failed; 70 for (i = 0; i < dc->num_threads; i++) 71 { 72 dc->threads[i].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread); 73 dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */ 74 dc->threads[i].curr_prio = spi->ti[i].dwCurrentPriority; 75 } 76 HeapFree(GetProcessHeap(), 0, pcs_buffer); 77 return TRUE; 78 } 79 if (!spi->NextEntryOffset) break; 80 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset); 81 } 82 } 83 failed: 84 HeapFree(GetProcessHeap(), 0, pcs_buffer); 85 return FALSE; 86 } 87 88 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr, 89 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd) 90 { 91 NT_TIB tib; 92 ADDRESS64 addr; 93 94 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL) && 95 dbghelp_current_cpu && 96 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat) 97 { 98 if (addr.Offset) 99 { 100 addr.Offset -= dbghelp_current_cpu->word_size; 101 /* make sure stack pointer is within the established range of the stack. It could have 102 been clobbered by whatever caused the original exception. */ 103 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase) 104 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 105 106 else 107 mmd->StartOfMemoryRange = addr.Offset; 108 } 109 else 110 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 111 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange; 112 } 113 } 114 115 /****************************************************************** 116 * fetch_thread_info 117 * 118 * fetches some information about thread of id 'tid' 119 */ 120 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx, 121 const MINIDUMP_EXCEPTION_INFORMATION* except, 122 MINIDUMP_THREAD* mdThd, CONTEXT* ctx) 123 { 124 DWORD tid = dc->threads[thd_idx].tid; 125 HANDLE hThread; 126 THREAD_BASIC_INFORMATION tbi; 127 128 memset(ctx, 0, sizeof(*ctx)); 129 130 mdThd->ThreadId = tid; 131 mdThd->SuspendCount = 0; 132 mdThd->Teb = 0; 133 mdThd->Stack.StartOfMemoryRange = 0; 134 mdThd->Stack.Memory.DataSize = 0; 135 mdThd->Stack.Memory.Rva = 0; 136 mdThd->ThreadContext.DataSize = 0; 137 mdThd->ThreadContext.Rva = 0; 138 mdThd->PriorityClass = dc->threads[thd_idx].prio_class; 139 mdThd->Priority = dc->threads[thd_idx].curr_prio; 140 141 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL) 142 { 143 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError()); 144 return FALSE; 145 } 146 147 if (NtQueryInformationThread(hThread, ThreadBasicInformation, 148 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS) 149 { 150 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress; 151 if (tbi.ExitStatus == STILL_ACTIVE) 152 { 153 if (tid != GetCurrentThreadId() && 154 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1) 155 { 156 ctx->ContextFlags = CONTEXT_FULL; 157 if (!GetThreadContext(hThread, ctx)) 158 memset(ctx, 0, sizeof(*ctx)); 159 160 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack); 161 ResumeThread(hThread); 162 } 163 else if (tid == GetCurrentThreadId() && except) 164 { 165 CONTEXT lctx, *pctx; 166 mdThd->SuspendCount = 1; 167 if (except->ClientPointers) 168 { 169 EXCEPTION_POINTERS ep; 170 171 ReadProcessMemory(dc->hProcess, except->ExceptionPointers, 172 &ep, sizeof(ep), NULL); 173 ReadProcessMemory(dc->hProcess, ep.ContextRecord, 174 &lctx, sizeof(lctx), NULL); 175 pctx = &lctx; 176 } 177 else pctx = except->ExceptionPointers->ContextRecord; 178 179 *ctx = *pctx; 180 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack); 181 } 182 else mdThd->SuspendCount = 0; 183 } 184 } 185 CloseHandle(hThread); 186 return TRUE; 187 } 188 189 /****************************************************************** 190 * add_module 191 * 192 * Add a module to a dump context 193 */ 194 static BOOL add_module(struct dump_context* dc, const WCHAR* name, 195 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum, 196 BOOL is_elf) 197 { 198 if (!dc->modules) 199 { 200 dc->alloc_modules = 32; 201 dc->modules = HeapAlloc(GetProcessHeap(), 0, 202 dc->alloc_modules * sizeof(*dc->modules)); 203 } 204 else if(dc->num_modules >= dc->alloc_modules) 205 { 206 dc->alloc_modules *= 2; 207 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules, 208 dc->alloc_modules * sizeof(*dc->modules)); 209 } 210 if (!dc->modules) 211 { 212 dc->alloc_modules = dc->num_modules = 0; 213 return FALSE; 214 } 215 if (is_elf || 216 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base, 217 dc->modules[dc->num_modules].name, 218 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR))) 219 lstrcpynW(dc->modules[dc->num_modules].name, name, 220 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)); 221 dc->modules[dc->num_modules].base = base; 222 dc->modules[dc->num_modules].size = size; 223 dc->modules[dc->num_modules].timestamp = timestamp; 224 dc->modules[dc->num_modules].checksum = checksum; 225 dc->modules[dc->num_modules].is_elf = is_elf; 226 dc->num_modules++; 227 228 return TRUE; 229 } 230 231 /****************************************************************** 232 * fetch_pe_module_info_cb 233 * 234 * Callback for accumulating in dump_context a PE modules set 235 */ 236 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size, 237 PVOID user) 238 { 239 struct dump_context* dc = user; 240 IMAGE_NT_HEADERS nth; 241 242 if (!validate_addr64(base)) return FALSE; 243 244 if (pe_load_nt_header(dc->hProcess, base, &nth)) 245 add_module(user, name, base, size, 246 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum, 247 FALSE); 248 return TRUE; 249 } 250 251 /****************************************************************** 252 * fetch_elf_module_info_cb 253 * 254 * Callback for accumulating in dump_context an ELF modules set 255 */ 256 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base, 257 void* user) 258 { 259 struct dump_context* dc = user; 260 DWORD_PTR rbase; 261 DWORD size, checksum; 262 263 /* FIXME: there's no relevant timestamp on ELF modules */ 264 /* NB: if we have a non-null base from the live-target use it (whenever 265 * the ELF module is relocatable or not). If we have a null base (ELF 266 * module isn't relocatable) then grab its base address from ELF file 267 */ 268 if (!elf_fetch_file_info(name, &rbase, &size, &checksum)) 269 size = checksum = 0; 270 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE); 271 return TRUE; 272 } 273 274 /****************************************************************** 275 * fetch_macho_module_info_cb 276 * 277 * Callback for accumulating in dump_context a Mach-O modules set 278 */ 279 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base, 280 void* user) 281 { 282 struct dump_context* dc = (struct dump_context*)user; 283 DWORD_PTR rbase; 284 DWORD size, checksum; 285 286 /* FIXME: there's no relevant timestamp on Mach-O modules */ 287 /* NB: if we have a non-null base from the live-target use it. If we have 288 * a null base, then grab its base address from Mach-O file. 289 */ 290 if (!macho_fetch_file_info(dc->hProcess, name, base, &rbase, &size, &checksum)) 291 size = checksum = 0; 292 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE); 293 return TRUE; 294 } 295 296 static void fetch_modules_info(struct dump_context* dc) 297 { 298 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc); 299 /* Since we include ELF modules in a separate stream from the regular PE ones, 300 * we can always include those ELF modules (they don't eat lots of space) 301 * And it's always a good idea to have a trace of the loaded ELF modules for 302 * a given application in a post mortem debugging condition. 303 */ 304 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc); 305 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc); 306 } 307 308 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi) 309 { 310 DWORD handle; 311 DWORD sz; 312 static const WCHAR backslashW[] = {'\\', '\0'}; 313 314 memset(ffi, 0, sizeof(*ffi)); 315 if ((sz = GetFileVersionInfoSizeW(filename, &handle))) 316 { 317 void* info = HeapAlloc(GetProcessHeap(), 0, sz); 318 if (info && GetFileVersionInfoW(filename, handle, sz, info)) 319 { 320 VS_FIXEDFILEINFO* ptr; 321 UINT len; 322 323 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len)) 324 memcpy(ffi, ptr, min(len, sizeof(*ffi))); 325 } 326 HeapFree(GetProcessHeap(), 0, info); 327 } 328 } 329 330 /****************************************************************** 331 * minidump_add_memory_block 332 * 333 * Add a memory block to be dumped in a minidump 334 * If rva is non 0, it's the rva in the minidump where has to be stored 335 * also the rva of the memory block when written (this allows us to reference 336 * a memory block from outside the list of memory blocks). 337 */ 338 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva) 339 { 340 if (!dc->mem) 341 { 342 dc->alloc_mem = 32; 343 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem)); 344 } 345 else if (dc->num_mem >= dc->alloc_mem) 346 { 347 dc->alloc_mem *= 2; 348 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 349 dc->alloc_mem * sizeof(*dc->mem)); 350 } 351 if (dc->mem) 352 { 353 dc->mem[dc->num_mem].base = base; 354 dc->mem[dc->num_mem].size = size; 355 dc->mem[dc->num_mem].rva = rva; 356 dc->num_mem++; 357 } 358 else dc->num_mem = dc->alloc_mem = 0; 359 } 360 361 /****************************************************************** 362 * writeat 363 * 364 * Writes a chunk of data at a given position in the minidump 365 */ 366 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size) 367 { 368 DWORD written; 369 370 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN); 371 WriteFile(dc->hFile, data, size, &written, NULL); 372 } 373 374 /****************************************************************** 375 * append 376 * 377 * writes a new chunk of data to the minidump, increasing the current 378 * rva in dc 379 */ 380 static void append(struct dump_context* dc, const void* data, unsigned size) 381 { 382 writeat(dc, dc->rva, data, size); 383 dc->rva += size; 384 } 385 386 /****************************************************************** 387 * dump_exception_info 388 * 389 * Write in File the exception information from pcs 390 */ 391 static unsigned dump_exception_info(struct dump_context* dc, 392 const MINIDUMP_EXCEPTION_INFORMATION* except) 393 { 394 MINIDUMP_EXCEPTION_STREAM mdExcpt; 395 EXCEPTION_RECORD rec, *prec; 396 CONTEXT ctx, *pctx; 397 DWORD i; 398 399 mdExcpt.ThreadId = except->ThreadId; 400 mdExcpt.__alignment = 0; 401 if (except->ClientPointers) 402 { 403 EXCEPTION_POINTERS ep; 404 405 ReadProcessMemory(dc->hProcess, 406 except->ExceptionPointers, &ep, sizeof(ep), NULL); 407 ReadProcessMemory(dc->hProcess, 408 ep.ExceptionRecord, &rec, sizeof(rec), NULL); 409 ReadProcessMemory(dc->hProcess, 410 ep.ContextRecord, &ctx, sizeof(ctx), NULL); 411 prec = &rec; 412 pctx = &ctx; 413 } 414 else 415 { 416 prec = except->ExceptionPointers->ExceptionRecord; 417 pctx = except->ExceptionPointers->ContextRecord; 418 } 419 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode; 420 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags; 421 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord; 422 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress; 423 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters; 424 mdExcpt.ExceptionRecord.__unusedAlignment = 0; 425 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++) 426 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i]; 427 mdExcpt.ThreadContext.DataSize = sizeof(*pctx); 428 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt); 429 430 append(dc, &mdExcpt, sizeof(mdExcpt)); 431 append(dc, pctx, sizeof(*pctx)); 432 return sizeof(mdExcpt); 433 } 434 435 /****************************************************************** 436 * dump_modules 437 * 438 * Write in File the modules from pcs 439 */ 440 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf) 441 { 442 MINIDUMP_MODULE mdModule; 443 MINIDUMP_MODULE_LIST mdModuleList; 444 char tmp[1024]; 445 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp; 446 ULONG i, nmod; 447 RVA rva_base; 448 DWORD flags_out; 449 unsigned sz; 450 451 for (i = nmod = 0; i < dc->num_modules; i++) 452 { 453 if ((dc->modules[i].is_elf && dump_elf) || 454 (!dc->modules[i].is_elf && !dump_elf)) 455 nmod++; 456 } 457 458 mdModuleList.NumberOfModules = 0; 459 /* reserve space for mdModuleList 460 * FIXME: since we don't support 0 length arrays, we cannot use the 461 * size of mdModuleList 462 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file 463 */ 464 465 /* the stream size is just the size of the module index. It does not include the data for the 466 names of each module. *Technically* the names are supposed to go into the common string table 467 in the minidump file. Since each string is referenced by RVA they can all safely be located 468 anywhere between streams in the file, so the end of this stream is sufficient. */ 469 rva_base = dc->rva; 470 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod; 471 for (i = 0; i < dc->num_modules; i++) 472 { 473 if ((dc->modules[i].is_elf && !dump_elf) || 474 (!dc->modules[i].is_elf && dump_elf)) 475 continue; 476 477 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord; 478 if (dc->type & MiniDumpWithDataSegs) 479 flags_out |= ModuleWriteDataSeg; 480 if (dc->type & MiniDumpWithProcessThreadData) 481 flags_out |= ModuleWriteTlsData; 482 if (dc->type & MiniDumpWithCodeSegs) 483 flags_out |= ModuleWriteCodeSegs; 484 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR); 485 if (sizeof(ULONG) + ms->Length > sizeof(tmp)) 486 FIXME("Buffer overflow!!!\n"); 487 lstrcpyW(ms->Buffer, dc->modules[i].name); 488 489 if (dc->cb) 490 { 491 MINIDUMP_CALLBACK_INPUT cbin; 492 MINIDUMP_CALLBACK_OUTPUT cbout; 493 494 cbin.ProcessId = dc->pid; 495 cbin.ProcessHandle = dc->hProcess; 496 cbin.CallbackType = ModuleCallback; 497 498 cbin.u.Module.FullPath = ms->Buffer; 499 cbin.u.Module.BaseOfImage = dc->modules[i].base; 500 cbin.u.Module.SizeOfImage = dc->modules[i].size; 501 cbin.u.Module.CheckSum = dc->modules[i].checksum; 502 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp; 503 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo)); 504 cbin.u.Module.CvRecord = NULL; 505 cbin.u.Module.SizeOfCvRecord = 0; 506 cbin.u.Module.MiscRecord = NULL; 507 cbin.u.Module.SizeOfMiscRecord = 0; 508 509 cbout.u.ModuleWriteFlags = flags_out; 510 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) 511 continue; 512 flags_out &= cbout.u.ModuleWriteFlags; 513 } 514 if (flags_out & ModuleWriteModule) 515 { 516 /* fetch CPU dependent module info (like UNWIND_INFO) */ 517 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out); 518 519 mdModule.BaseOfImage = dc->modules[i].base; 520 mdModule.SizeOfImage = dc->modules[i].size; 521 mdModule.CheckSum = dc->modules[i].checksum; 522 mdModule.TimeDateStamp = dc->modules[i].timestamp; 523 mdModule.ModuleNameRva = dc->rva; 524 ms->Length -= sizeof(WCHAR); 525 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR)); 526 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo); 527 mdModule.CvRecord.DataSize = 0; /* FIXME */ 528 mdModule.CvRecord.Rva = 0; /* FIXME */ 529 mdModule.MiscRecord.DataSize = 0; /* FIXME */ 530 mdModule.MiscRecord.Rva = 0; /* FIXME */ 531 mdModule.Reserved0 = 0; /* FIXME */ 532 mdModule.Reserved1 = 0; /* FIXME */ 533 writeat(dc, 534 rva_base + sizeof(mdModuleList.NumberOfModules) + 535 mdModuleList.NumberOfModules++ * sizeof(mdModule), 536 &mdModule, sizeof(mdModule)); 537 } 538 } 539 writeat(dc, rva_base, &mdModuleList.NumberOfModules, 540 sizeof(mdModuleList.NumberOfModules)); 541 542 return sz; 543 } 544 545 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p 546 * We are compiled with -fPIC, so we can't clobber ebx. 547 */ 548 static inline void do_x86cpuid(unsigned int ax, unsigned int *p) 549 { 550 #if defined(__GNUC__) && defined(__i386__) 551 __asm__("pushl %%ebx\n\t" 552 "cpuid\n\t" 553 "movl %%ebx, %%esi\n\t" 554 "popl %%ebx" 555 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3]) 556 : "0" (ax)); 557 #endif 558 } 559 560 /* From xf86info havecpuid.c 1.11 */ 561 static inline int have_x86cpuid(void) 562 { 563 #if defined(__GNUC__) && defined(__i386__) 564 unsigned int f1, f2; 565 __asm__("pushfl\n\t" 566 "pushfl\n\t" 567 "popl %0\n\t" 568 "movl %0,%1\n\t" 569 "xorl %2,%0\n\t" 570 "pushl %0\n\t" 571 "popfl\n\t" 572 "pushfl\n\t" 573 "popl %0\n\t" 574 "popfl" 575 : "=&r" (f1), "=&r" (f2) 576 : "ir" (0x00200000)); 577 return ((f1^f2) & 0x00200000) != 0; 578 #else 579 return 0; 580 #endif 581 } 582 583 /****************************************************************** 584 * dump_system_info 585 * 586 * Dumps into File the information about the system 587 */ 588 static unsigned dump_system_info(struct dump_context* dc) 589 { 590 MINIDUMP_SYSTEM_INFO mdSysInfo; 591 SYSTEM_INFO sysInfo; 592 OSVERSIONINFOW osInfo; 593 DWORD written; 594 ULONG slen; 595 DWORD wine_extra = 0; 596 597 const char *(CDECL *wine_get_build_id)(void); 598 void (CDECL *wine_get_host_version)(const char **sysname, const char **release); 599 const char* build_id = NULL; 600 const char* sys_name = NULL; 601 const char* release_name = NULL; 602 603 GetSystemInfo(&sysInfo); 604 osInfo.dwOSVersionInfoSize = sizeof(osInfo); 605 GetVersionExW(&osInfo); 606 607 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id"); 608 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version"); 609 if (wine_get_build_id && wine_get_host_version) 610 { 611 /* cheat minidump system information by adding specific wine information */ 612 wine_extra = 4 + 4 * sizeof(slen); 613 build_id = wine_get_build_id(); 614 wine_get_host_version(&sys_name, &release_name); 615 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1; 616 } 617 618 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture; 619 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel; 620 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision; 621 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors; 622 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */ 623 mdSysInfo.MajorVersion = osInfo.dwMajorVersion; 624 mdSysInfo.MinorVersion = osInfo.dwMinorVersion; 625 mdSysInfo.BuildNumber = osInfo.dwBuildNumber; 626 mdSysInfo.PlatformId = osInfo.dwPlatformId; 627 628 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra; 629 mdSysInfo.u1.Reserved1 = 0; 630 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL; 631 632 if (have_x86cpuid()) 633 { 634 unsigned regs0[4], regs1[4]; 635 636 do_x86cpuid(0, regs0); 637 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1]; 638 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3]; 639 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2]; 640 do_x86cpuid(1, regs1); 641 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0]; 642 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3]; 643 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0; 644 if (regs0[1] == 0x68747541 /* "Auth" */ && 645 regs0[3] == 0x69746e65 /* "enti" */ && 646 regs0[2] == 0x444d4163 /* "cAMD" */) 647 { 648 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */ 649 if (regs1[0] >= 0x80000001) 650 { 651 do_x86cpuid(0x80000001, regs1); /* get vendor features */ 652 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3]; 653 } 654 } 655 } 656 else 657 { 658 unsigned i; 659 ULONG64 one = 1; 660 661 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0; 662 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; 663 664 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++) 665 if (IsProcessorFeaturePresent(i)) 666 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i; 667 } 668 append(dc, &mdSysInfo, sizeof(mdSysInfo)); 669 670 /* write Wine specific system information just behind the structure, and before any string */ 671 if (wine_extra) 672 { 673 char code[] = {'W','I','N','E'}; 674 675 WriteFile(dc->hFile, code, 4, &written, NULL); 676 /* number of sub-info, so that we can extend structure if needed */ 677 slen = 3; 678 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 679 /* we store offsets from just after the WINE marker */ 680 slen = 4 * sizeof(DWORD); 681 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 682 slen += strlen(build_id) + 1; 683 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 684 slen += strlen(sys_name) + 1; 685 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 686 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL); 687 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL); 688 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL); 689 dc->rva += wine_extra; 690 } 691 692 /* write the service pack version string after this stream. It is referenced within the 693 stream by its RVA in the file. */ 694 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR); 695 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 696 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL); 697 dc->rva += sizeof(ULONG) + slen; 698 699 return sizeof(mdSysInfo); 700 } 701 702 /****************************************************************** 703 * dump_threads 704 * 705 * Dumps into File the information about running threads 706 */ 707 static unsigned dump_threads(struct dump_context* dc, 708 const MINIDUMP_EXCEPTION_INFORMATION* except) 709 { 710 MINIDUMP_THREAD mdThd; 711 MINIDUMP_THREAD_LIST mdThdList; 712 unsigned i, sz; 713 RVA rva_base; 714 DWORD flags_out; 715 CONTEXT ctx; 716 717 mdThdList.NumberOfThreads = 0; 718 719 rva_base = dc->rva; 720 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd); 721 722 for (i = 0; i < dc->num_threads; i++) 723 { 724 fetch_thread_info(dc, i, except, &mdThd, &ctx); 725 726 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | 727 ThreadWriteInstructionWindow; 728 if (dc->type & MiniDumpWithProcessThreadData) 729 flags_out |= ThreadWriteThreadData; 730 if (dc->type & MiniDumpWithThreadInfo) 731 flags_out |= ThreadWriteThreadInfo; 732 733 if (dc->cb) 734 { 735 MINIDUMP_CALLBACK_INPUT cbin; 736 MINIDUMP_CALLBACK_OUTPUT cbout; 737 738 cbin.ProcessId = dc->pid; 739 cbin.ProcessHandle = dc->hProcess; 740 cbin.CallbackType = ThreadCallback; 741 cbin.u.Thread.ThreadId = dc->threads[i].tid; 742 cbin.u.Thread.ThreadHandle = 0; /* FIXME */ 743 cbin.u.Thread.Context = ctx; 744 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT); 745 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange; 746 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange + 747 mdThd.Stack.Memory.DataSize; 748 749 cbout.u.ThreadWriteFlags = flags_out; 750 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) 751 continue; 752 flags_out &= cbout.u.ThreadWriteFlags; 753 } 754 if (flags_out & ThreadWriteThread) 755 { 756 if (ctx.ContextFlags && (flags_out & ThreadWriteContext)) 757 { 758 mdThd.ThreadContext.Rva = dc->rva; 759 mdThd.ThreadContext.DataSize = sizeof(CONTEXT); 760 append(dc, &ctx, sizeof(CONTEXT)); 761 } 762 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack)) 763 { 764 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange, 765 mdThd.Stack.Memory.DataSize, 766 rva_base + sizeof(mdThdList.NumberOfThreads) + 767 mdThdList.NumberOfThreads * sizeof(mdThd) + 768 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva)); 769 } 770 writeat(dc, 771 rva_base + sizeof(mdThdList.NumberOfThreads) + 772 mdThdList.NumberOfThreads * sizeof(mdThd), 773 &mdThd, sizeof(mdThd)); 774 mdThdList.NumberOfThreads++; 775 } 776 /* fetch CPU dependent thread info (like 256 bytes around program counter */ 777 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx); 778 } 779 writeat(dc, rva_base, 780 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads)); 781 782 return sz; 783 } 784 785 /****************************************************************** 786 * dump_memory_info 787 * 788 * dumps information about the memory of the process (stack of the threads) 789 */ 790 static unsigned dump_memory_info(struct dump_context* dc) 791 { 792 MINIDUMP_MEMORY_LIST mdMemList; 793 MINIDUMP_MEMORY_DESCRIPTOR mdMem; 794 DWORD written; 795 unsigned i, pos, len, sz; 796 RVA rva_base; 797 char tmp[1024]; 798 799 mdMemList.NumberOfMemoryRanges = dc->num_mem; 800 append(dc, &mdMemList.NumberOfMemoryRanges, 801 sizeof(mdMemList.NumberOfMemoryRanges)); 802 rva_base = dc->rva; 803 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem); 804 dc->rva += sz; 805 sz += sizeof(mdMemList.NumberOfMemoryRanges); 806 807 for (i = 0; i < dc->num_mem; i++) 808 { 809 mdMem.StartOfMemoryRange = dc->mem[i].base; 810 mdMem.Memory.Rva = dc->rva; 811 mdMem.Memory.DataSize = dc->mem[i].size; 812 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN); 813 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp)) 814 { 815 len = min(dc->mem[i].size - pos, sizeof(tmp)); 816 if (ReadProcessMemory(dc->hProcess, 817 (void*)(DWORD_PTR)(dc->mem[i].base + pos), 818 tmp, len, NULL)) 819 WriteFile(dc->hFile, tmp, len, &written, NULL); 820 } 821 dc->rva += mdMem.Memory.DataSize; 822 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem)); 823 if (dc->mem[i].rva) 824 { 825 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva)); 826 } 827 } 828 829 return sz; 830 } 831 832 static unsigned dump_misc_info(struct dump_context* dc) 833 { 834 MINIDUMP_MISC_INFO mmi; 835 836 mmi.SizeOfInfo = sizeof(mmi); 837 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID; 838 mmi.ProcessId = dc->pid; 839 /* FIXME: create/user/kernel time */ 840 mmi.ProcessCreateTime = 0; 841 mmi.ProcessKernelTime = 0; 842 mmi.ProcessUserTime = 0; 843 844 append(dc, &mmi, sizeof(mmi)); 845 return sizeof(mmi); 846 } 847 848 /****************************************************************** 849 * MiniDumpWriteDump (DEBUGHLP.@) 850 * 851 */ 852 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, 853 MINIDUMP_TYPE DumpType, 854 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 855 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 856 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) 857 { 858 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}}; 859 MINIDUMP_HEADER mdHead; 860 MINIDUMP_DIRECTORY mdDir; 861 DWORD i, nStreams, idx_stream; 862 struct dump_context dc; 863 864 dc.hProcess = hProcess; 865 dc.hFile = hFile; 866 dc.pid = pid; 867 dc.modules = NULL; 868 dc.num_modules = 0; 869 dc.alloc_modules = 0; 870 dc.threads = NULL; 871 dc.num_threads = 0; 872 dc.cb = CallbackParam; 873 dc.type = DumpType; 874 dc.mem = NULL; 875 dc.num_mem = 0; 876 dc.alloc_mem = 0; 877 dc.rva = 0; 878 879 if (!fetch_process_info(&dc)) return FALSE; 880 fetch_modules_info(&dc); 881 882 /* 1) init */ 883 nStreams = 6 + (ExceptionParam ? 1 : 0) + 884 (UserStreamParam ? UserStreamParam->UserStreamCount : 0); 885 886 /* pad the directory size to a multiple of 4 for alignment purposes */ 887 nStreams = (nStreams + 3) & ~3; 888 889 if (DumpType & MiniDumpWithDataSegs) 890 FIXME("NIY MiniDumpWithDataSegs\n"); 891 if (DumpType & MiniDumpWithFullMemory) 892 FIXME("NIY MiniDumpWithFullMemory\n"); 893 if (DumpType & MiniDumpWithHandleData) 894 FIXME("NIY MiniDumpWithHandleData\n"); 895 if (DumpType & MiniDumpFilterMemory) 896 FIXME("NIY MiniDumpFilterMemory\n"); 897 if (DumpType & MiniDumpScanMemory) 898 FIXME("NIY MiniDumpScanMemory\n"); 899 900 /* 2) write header */ 901 mdHead.Signature = MINIDUMP_SIGNATURE; 902 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */ 903 mdHead.NumberOfStreams = nStreams; 904 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */ 905 mdHead.StreamDirectoryRva = sizeof(mdHead); 906 mdHead.u.TimeDateStamp = time(NULL); 907 mdHead.Flags = DumpType; 908 append(&dc, &mdHead, sizeof(mdHead)); 909 910 /* 3) write stream directories */ 911 dc.rva += nStreams * sizeof(mdDir); 912 idx_stream = 0; 913 914 /* 3.1) write data stream directories */ 915 916 /* must be first in minidump */ 917 mdDir.StreamType = SystemInfoStream; 918 mdDir.Location.Rva = dc.rva; 919 mdDir.Location.DataSize = dump_system_info(&dc); 920 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 921 &mdDir, sizeof(mdDir)); 922 923 mdDir.StreamType = ThreadListStream; 924 mdDir.Location.Rva = dc.rva; 925 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam); 926 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 927 &mdDir, sizeof(mdDir)); 928 929 mdDir.StreamType = ModuleListStream; 930 mdDir.Location.Rva = dc.rva; 931 mdDir.Location.DataSize = dump_modules(&dc, FALSE); 932 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 933 &mdDir, sizeof(mdDir)); 934 935 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */ 936 mdDir.Location.Rva = dc.rva; 937 mdDir.Location.DataSize = dump_modules(&dc, TRUE); 938 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 939 &mdDir, sizeof(mdDir)); 940 941 mdDir.StreamType = MemoryListStream; 942 mdDir.Location.Rva = dc.rva; 943 mdDir.Location.DataSize = dump_memory_info(&dc); 944 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 945 &mdDir, sizeof(mdDir)); 946 947 mdDir.StreamType = MiscInfoStream; 948 mdDir.Location.Rva = dc.rva; 949 mdDir.Location.DataSize = dump_misc_info(&dc); 950 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 951 &mdDir, sizeof(mdDir)); 952 953 /* 3.2) write exception information (if any) */ 954 if (ExceptionParam) 955 { 956 mdDir.StreamType = ExceptionStream; 957 mdDir.Location.Rva = dc.rva; 958 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam); 959 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 960 &mdDir, sizeof(mdDir)); 961 } 962 963 /* 3.3) write user defined streams (if any) */ 964 if (UserStreamParam) 965 { 966 for (i = 0; i < UserStreamParam->UserStreamCount; i++) 967 { 968 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type; 969 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize; 970 mdDir.Location.Rva = dc.rva; 971 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 972 &mdDir, sizeof(mdDir)); 973 append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 974 UserStreamParam->UserStreamArray[i].BufferSize); 975 } 976 } 977 978 /* fill the remaining directory entries with 0's (unused stream types) */ 979 /* NOTE: this should always come last in the dump! */ 980 for (i = idx_stream; i < nStreams; i++) 981 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir)); 982 983 HeapFree(GetProcessHeap(), 0, dc.mem); 984 HeapFree(GetProcessHeap(), 0, dc.modules); 985 HeapFree(GetProcessHeap(), 0, dc.threads); 986 987 return TRUE; 988 } 989 990 /****************************************************************** 991 * MiniDumpReadDumpStream (DEBUGHLP.@) 992 * 993 * 994 */ 995 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx, 996 PMINIDUMP_DIRECTORY* pdir, 997 PVOID* stream, ULONG* size) 998 { 999 MINIDUMP_HEADER* mdHead = base; 1000 1001 if (mdHead->Signature == MINIDUMP_SIGNATURE) 1002 { 1003 MINIDUMP_DIRECTORY* dir; 1004 DWORD i; 1005 1006 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva); 1007 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++) 1008 { 1009 if (dir->StreamType == str_idx) 1010 { 1011 if (pdir) *pdir = dir; 1012 if (stream) *stream = (char*)base + dir->Location.Rva; 1013 if (size) *size = dir->Location.DataSize; 1014 return TRUE; 1015 } 1016 } 1017 } 1018 SetLastError(ERROR_INVALID_PARAMETER); 1019 return FALSE; 1020 } 1021