minidump-2-core.cc revision af0cd7cb05ea70d6c19ec83235b7a87e735d2b27
1// Copyright (c) 2009, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30// Converts a minidump file to a core file which gdb can read. 31// Large parts lifted from the userspace core dumper: 32// http://code.google.com/p/google-coredumper/ 33// 34// Usage: minidump-2-core [-v] 1234.dmp > core 35 36#include <elf.h> 37#include <errno.h> 38#include <link.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <sys/user.h> 43#include <unistd.h> 44 45#include <map> 46#include <string> 47#include <vector> 48 49#include "common/linux/memory_mapped_file.h" 50#include "common/scoped_ptr.h" 51#include "google_breakpad/common/minidump_format.h" 52#include "third_party/lss/linux_syscall_support.h" 53#include "tools/linux/md2core/minidump_memory_range.h" 54 55#if __WORDSIZE == 64 56 #define ELF_CLASS ELFCLASS64 57#else 58 #define ELF_CLASS ELFCLASS32 59#endif 60#define Ehdr ElfW(Ehdr) 61#define Phdr ElfW(Phdr) 62#define Shdr ElfW(Shdr) 63#define Nhdr ElfW(Nhdr) 64#define auxv_t ElfW(auxv_t) 65 66 67#if defined(__x86_64__) 68 #define ELF_ARCH EM_X86_64 69#elif defined(__i386__) 70 #define ELF_ARCH EM_386 71#elif defined(__arm__) 72 #define ELF_ARCH EM_ARM 73#elif defined(__mips__) 74 #define ELF_ARCH EM_MIPS 75#endif 76 77#if defined(__arm__) 78// GLibc/ARM and Android/ARM both use 'user_regs' for the structure type 79// containing core registers, while they use 'user_regs_struct' on other 80// architectures. This file-local typedef simplifies the source code. 81typedef user_regs user_regs_struct; 82#endif 83 84using google_breakpad::MemoryMappedFile; 85using google_breakpad::MinidumpMemoryRange; 86 87static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1); 88static bool verbose; 89static std::string g_custom_so_basedir; 90 91static int usage(const char* argv0) { 92 fprintf(stderr, "Usage: %s [-v] <minidump file>\n", argv0); 93 return 1; 94} 95 96// Write all of the given buffer, handling short writes and EINTR. Return true 97// iff successful. 98static bool 99writea(int fd, const void* idata, size_t length) { 100 const uint8_t* data = (const uint8_t*) idata; 101 102 size_t done = 0; 103 while (done < length) { 104 ssize_t r; 105 do { 106 r = write(fd, data + done, length - done); 107 } while (r == -1 && errno == EINTR); 108 109 if (r < 1) 110 return false; 111 done += r; 112 } 113 114 return true; 115} 116 117/* Dynamically determines the byte sex of the system. Returns non-zero 118 * for big-endian machines. 119 */ 120static inline int sex() { 121 int probe = 1; 122 return !*(char *)&probe; 123} 124 125typedef struct elf_timeval { /* Time value with microsecond resolution */ 126 long tv_sec; /* Seconds */ 127 long tv_usec; /* Microseconds */ 128} elf_timeval; 129 130typedef struct elf_siginfo { /* Information about signal (unused) */ 131 int32_t si_signo; /* Signal number */ 132 int32_t si_code; /* Extra code */ 133 int32_t si_errno; /* Errno */ 134} elf_siginfo; 135 136typedef struct prstatus { /* Information about thread; includes CPU reg*/ 137 elf_siginfo pr_info; /* Info associated with signal */ 138 uint16_t pr_cursig; /* Current signal */ 139 unsigned long pr_sigpend; /* Set of pending signals */ 140 unsigned long pr_sighold; /* Set of held signals */ 141 pid_t pr_pid; /* Process ID */ 142 pid_t pr_ppid; /* Parent's process ID */ 143 pid_t pr_pgrp; /* Group ID */ 144 pid_t pr_sid; /* Session ID */ 145 elf_timeval pr_utime; /* User time */ 146 elf_timeval pr_stime; /* System time */ 147 elf_timeval pr_cutime; /* Cumulative user time */ 148 elf_timeval pr_cstime; /* Cumulative system time */ 149 user_regs_struct pr_reg; /* CPU registers */ 150 uint32_t pr_fpvalid; /* True if math co-processor being used */ 151} prstatus; 152 153typedef struct prpsinfo { /* Information about process */ 154 unsigned char pr_state; /* Numeric process state */ 155 char pr_sname; /* Char for pr_state */ 156 unsigned char pr_zomb; /* Zombie */ 157 signed char pr_nice; /* Nice val */ 158 unsigned long pr_flag; /* Flags */ 159#if defined(__x86_64__) || defined(__mips__) 160 uint32_t pr_uid; /* User ID */ 161 uint32_t pr_gid; /* Group ID */ 162#else 163 uint16_t pr_uid; /* User ID */ 164 uint16_t pr_gid; /* Group ID */ 165#endif 166 pid_t pr_pid; /* Process ID */ 167 pid_t pr_ppid; /* Parent's process ID */ 168 pid_t pr_pgrp; /* Group ID */ 169 pid_t pr_sid; /* Session ID */ 170 char pr_fname[16]; /* Filename of executable */ 171 char pr_psargs[80]; /* Initial part of arg list */ 172} prpsinfo; 173 174// We parse the minidump file and keep the parsed information in this structure 175struct CrashedProcess { 176 CrashedProcess() 177 : crashing_tid(-1), 178 auxv(NULL), 179 auxv_length(0) { 180 memset(&prps, 0, sizeof(prps)); 181 prps.pr_sname = 'R'; 182 memset(&debug, 0, sizeof(debug)); 183 } 184 185 struct Mapping { 186 Mapping() 187 : permissions(0xFFFFFFFF), 188 start_address(0), 189 end_address(0), 190 offset(0) { 191 } 192 193 uint32_t permissions; 194 uint64_t start_address, end_address, offset; 195 std::string filename; 196 std::string data; 197 }; 198 std::map<uint64_t, Mapping> mappings; 199 200 pid_t crashing_tid; 201 int fatal_signal; 202 203 struct Thread { 204 pid_t tid; 205 user_regs_struct regs; 206#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) 207 user_fpregs_struct fpregs; 208#endif 209#if defined(__i386__) 210 user_fpxregs_struct fpxregs; 211#endif 212 uintptr_t stack_addr; 213 const uint8_t* stack; 214 size_t stack_length; 215 }; 216 std::vector<Thread> threads; 217 218 const uint8_t* auxv; 219 size_t auxv_length; 220 221 prpsinfo prps; 222 223 std::map<uintptr_t, std::string> signatures; 224 225 std::string dynamic_data; 226 MDRawDebug debug; 227 std::vector<MDRawLinkMap> link_map; 228}; 229 230#if defined(__i386__) 231static uint32_t 232U32(const uint8_t* data) { 233 uint32_t v; 234 memcpy(&v, data, sizeof(v)); 235 return v; 236} 237 238static uint16_t 239U16(const uint8_t* data) { 240 uint16_t v; 241 memcpy(&v, data, sizeof(v)); 242 return v; 243} 244 245static void 246ParseThreadRegisters(CrashedProcess::Thread* thread, 247 const MinidumpMemoryRange& range) { 248 const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0); 249 250 thread->regs.ebx = rawregs->ebx; 251 thread->regs.ecx = rawregs->ecx; 252 thread->regs.edx = rawregs->edx; 253 thread->regs.esi = rawregs->esi; 254 thread->regs.edi = rawregs->edi; 255 thread->regs.ebp = rawregs->ebp; 256 thread->regs.eax = rawregs->eax; 257 thread->regs.xds = rawregs->ds; 258 thread->regs.xes = rawregs->es; 259 thread->regs.xfs = rawregs->fs; 260 thread->regs.xgs = rawregs->gs; 261 thread->regs.orig_eax = rawregs->eax; 262 thread->regs.eip = rawregs->eip; 263 thread->regs.xcs = rawregs->cs; 264 thread->regs.eflags = rawregs->eflags; 265 thread->regs.esp = rawregs->esp; 266 thread->regs.xss = rawregs->ss; 267 268 thread->fpregs.cwd = rawregs->float_save.control_word; 269 thread->fpregs.swd = rawregs->float_save.status_word; 270 thread->fpregs.twd = rawregs->float_save.tag_word; 271 thread->fpregs.fip = rawregs->float_save.error_offset; 272 thread->fpregs.fcs = rawregs->float_save.error_selector; 273 thread->fpregs.foo = rawregs->float_save.data_offset; 274 thread->fpregs.fos = rawregs->float_save.data_selector; 275 memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, 276 10 * 8); 277 278 thread->fpxregs.cwd = rawregs->float_save.control_word; 279 thread->fpxregs.swd = rawregs->float_save.status_word; 280 thread->fpxregs.twd = rawregs->float_save.tag_word; 281 thread->fpxregs.fop = U16(rawregs->extended_registers + 6); 282 thread->fpxregs.fip = U16(rawregs->extended_registers + 8); 283 thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); 284 thread->fpxregs.foo = U16(rawregs->extended_registers + 16); 285 thread->fpxregs.fos = U16(rawregs->extended_registers + 20); 286 thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); 287 memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); 288 memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); 289} 290#elif defined(__x86_64__) 291static void 292ParseThreadRegisters(CrashedProcess::Thread* thread, 293 const MinidumpMemoryRange& range) { 294 const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0); 295 296 thread->regs.r15 = rawregs->r15; 297 thread->regs.r14 = rawregs->r14; 298 thread->regs.r13 = rawregs->r13; 299 thread->regs.r12 = rawregs->r12; 300 thread->regs.rbp = rawregs->rbp; 301 thread->regs.rbx = rawregs->rbx; 302 thread->regs.r11 = rawregs->r11; 303 thread->regs.r10 = rawregs->r10; 304 thread->regs.r9 = rawregs->r9; 305 thread->regs.r8 = rawregs->r8; 306 thread->regs.rax = rawregs->rax; 307 thread->regs.rcx = rawregs->rcx; 308 thread->regs.rdx = rawregs->rdx; 309 thread->regs.rsi = rawregs->rsi; 310 thread->regs.rdi = rawregs->rdi; 311 thread->regs.orig_rax = rawregs->rax; 312 thread->regs.rip = rawregs->rip; 313 thread->regs.cs = rawregs->cs; 314 thread->regs.eflags = rawregs->eflags; 315 thread->regs.rsp = rawregs->rsp; 316 thread->regs.ss = rawregs->ss; 317 thread->regs.fs_base = 0; 318 thread->regs.gs_base = 0; 319 thread->regs.ds = rawregs->ds; 320 thread->regs.es = rawregs->es; 321 thread->regs.fs = rawregs->fs; 322 thread->regs.gs = rawregs->gs; 323 324 thread->fpregs.cwd = rawregs->flt_save.control_word; 325 thread->fpregs.swd = rawregs->flt_save.status_word; 326 thread->fpregs.ftw = rawregs->flt_save.tag_word; 327 thread->fpregs.fop = rawregs->flt_save.error_opcode; 328 thread->fpregs.rip = rawregs->flt_save.error_offset; 329 thread->fpregs.rdp = rawregs->flt_save.data_offset; 330 thread->fpregs.mxcsr = rawregs->flt_save.mx_csr; 331 thread->fpregs.mxcr_mask = rawregs->flt_save.mx_csr_mask; 332 memcpy(thread->fpregs.st_space, rawregs->flt_save.float_registers, 8 * 16); 333 memcpy(thread->fpregs.xmm_space, rawregs->flt_save.xmm_registers, 16 * 16); 334} 335#elif defined(__arm__) 336static void 337ParseThreadRegisters(CrashedProcess::Thread* thread, 338 const MinidumpMemoryRange& range) { 339 const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0); 340 341 thread->regs.uregs[0] = rawregs->iregs[0]; 342 thread->regs.uregs[1] = rawregs->iregs[1]; 343 thread->regs.uregs[2] = rawregs->iregs[2]; 344 thread->regs.uregs[3] = rawregs->iregs[3]; 345 thread->regs.uregs[4] = rawregs->iregs[4]; 346 thread->regs.uregs[5] = rawregs->iregs[5]; 347 thread->regs.uregs[6] = rawregs->iregs[6]; 348 thread->regs.uregs[7] = rawregs->iregs[7]; 349 thread->regs.uregs[8] = rawregs->iregs[8]; 350 thread->regs.uregs[9] = rawregs->iregs[9]; 351 thread->regs.uregs[10] = rawregs->iregs[10]; 352 thread->regs.uregs[11] = rawregs->iregs[11]; 353 thread->regs.uregs[12] = rawregs->iregs[12]; 354 thread->regs.uregs[13] = rawregs->iregs[13]; 355 thread->regs.uregs[14] = rawregs->iregs[14]; 356 thread->regs.uregs[15] = rawregs->iregs[15]; 357 358 thread->regs.uregs[16] = rawregs->cpsr; 359 thread->regs.uregs[17] = 0; // what is ORIG_r0 exactly? 360} 361#elif defined(__mips__) 362static void 363ParseThreadRegisters(CrashedProcess::Thread* thread, 364 const MinidumpMemoryRange& range) { 365 const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0); 366 367 for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) 368 thread->regs.regs[i] = rawregs->iregs[i]; 369 370 thread->regs.lo = rawregs->mdlo; 371 thread->regs.hi = rawregs->mdhi; 372 thread->regs.epc = rawregs->epc; 373 thread->regs.badvaddr = rawregs->badvaddr; 374 thread->regs.status = rawregs->status; 375 thread->regs.cause = rawregs->cause; 376 377 for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) 378 thread->fpregs.regs[i] = rawregs->float_save.regs[i]; 379 380 thread->fpregs.fpcsr = rawregs->float_save.fpcsr; 381 thread->fpregs.fir = rawregs->float_save.fir; 382} 383#else 384#error "This code has not been ported to your platform yet" 385#endif 386 387static void 388ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, 389 const MinidumpMemoryRange& full_file) { 390 const uint32_t num_threads = *range.GetData<uint32_t>(0); 391 if (verbose) { 392 fprintf(stderr, 393 "MD_THREAD_LIST_STREAM:\n" 394 "Found %d threads\n" 395 "\n\n", 396 num_threads); 397 } 398 for (unsigned i = 0; i < num_threads; ++i) { 399 CrashedProcess::Thread thread; 400 memset(&thread, 0, sizeof(thread)); 401 const MDRawThread* rawthread = 402 range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i); 403 thread.tid = rawthread->thread_id; 404 thread.stack_addr = rawthread->stack.start_of_memory_range; 405 MinidumpMemoryRange stack_range = 406 full_file.Subrange(rawthread->stack.memory); 407 thread.stack = stack_range.data(); 408 thread.stack_length = rawthread->stack.memory.data_size; 409 410 ParseThreadRegisters(&thread, 411 full_file.Subrange(rawthread->thread_context)); 412 413 crashinfo->threads.push_back(thread); 414 } 415} 416 417static void 418ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, 419 const MinidumpMemoryRange& full_file) { 420 const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0); 421 if (!sysinfo) { 422 fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n"); 423 _exit(1); 424 } 425#if defined(__i386__) 426 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) { 427 fprintf(stderr, 428 "This version of minidump-2-core only supports x86 (32bit)%s.\n", 429 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ? 430 ",\nbut the minidump file is from a 64bit machine" : ""); 431 _exit(1); 432 } 433#elif defined(__x86_64__) 434 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) { 435 fprintf(stderr, 436 "This version of minidump-2-core only supports x86 (64bit)%s.\n", 437 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ? 438 ",\nbut the minidump file is from a 32bit machine" : ""); 439 _exit(1); 440 } 441#elif defined(__arm__) 442 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) { 443 fprintf(stderr, 444 "This version of minidump-2-core only supports ARM (32bit).\n"); 445 _exit(1); 446 } 447#elif defined(__mips__) 448 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) { 449 fprintf(stderr, 450 "This version of minidump-2-core only supports mips (32bit).\n"); 451 _exit(1); 452 } 453#else 454#error "This code has not been ported to your platform yet" 455#endif 456 if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(), 457 "Linux") && 458 sysinfo->platform_id != MD_OS_NACL) { 459 fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n"); 460 _exit(1); 461 } 462 463 if (verbose) { 464 fprintf(stderr, 465 "MD_SYSTEM_INFO_STREAM:\n" 466 "Architecture: %s\n" 467 "Number of processors: %d\n" 468 "Processor level: %d\n" 469 "Processor model: %d\n" 470 "Processor stepping: %d\n", 471 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 472 ? "i386" 473 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 474 ? "x86-64" 475 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM 476 ? "ARM" 477 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS 478 ? "MIPS" 479 : "???", 480 sysinfo->number_of_processors, 481 sysinfo->processor_level, 482 sysinfo->processor_revision >> 8, 483 sysinfo->processor_revision & 0xFF); 484 if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 || 485 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) { 486 fputs("Vendor id: ", stderr); 487 const char *nul = 488 (const char *)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0, 489 sizeof(sysinfo->cpu.x86_cpu_info.vendor_id)); 490 fwrite(sysinfo->cpu.x86_cpu_info.vendor_id, 491 nul ? nul - (const char *)&sysinfo->cpu.x86_cpu_info.vendor_id[0] 492 : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr); 493 fputs("\n", stderr); 494 } 495 fprintf(stderr, "OS: %s\n", 496 full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str()); 497 fputs("\n\n", stderr); 498 } 499} 500 501static void 502ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 503 if (verbose) { 504 fputs("MD_LINUX_CPU_INFO:\n", stderr); 505 fwrite(range.data(), range.length(), 1, stderr); 506 fputs("\n\n\n", stderr); 507 } 508} 509 510static void 511ParseProcessStatus(CrashedProcess* crashinfo, 512 const MinidumpMemoryRange& range) { 513 if (verbose) { 514 fputs("MD_LINUX_PROC_STATUS:\n", stderr); 515 fwrite(range.data(), range.length(), 1, stderr); 516 fputs("\n\n", stderr); 517 } 518} 519 520static void 521ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 522 if (verbose) { 523 fputs("MD_LINUX_LSB_RELEASE:\n", stderr); 524 fwrite(range.data(), range.length(), 1, stderr); 525 fputs("\n\n", stderr); 526 } 527} 528 529static void 530ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 531 if (verbose) { 532 fputs("MD_LINUX_MAPS:\n", stderr); 533 fwrite(range.data(), range.length(), 1, stderr); 534 } 535 for (const uint8_t* ptr = range.data(); 536 ptr < range.data() + range.length();) { 537 const uint8_t* eol = (uint8_t*)memchr(ptr, '\n', 538 range.data() + range.length() - ptr); 539 std::string line((const char*)ptr, 540 eol ? eol - ptr : range.data() + range.length() - ptr); 541 ptr = eol ? eol + 1 : range.data() + range.length(); 542 unsigned long long start, stop, offset; 543 char* permissions = NULL; 544 char* filename = NULL; 545 sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms", 546 &start, &stop, &permissions, &offset, &filename); 547 if (filename && *filename == '/') { 548 CrashedProcess::Mapping mapping; 549 mapping.permissions = 0; 550 if (strchr(permissions, 'r')) { 551 mapping.permissions |= PF_R; 552 } 553 if (strchr(permissions, 'w')) { 554 mapping.permissions |= PF_W; 555 } 556 if (strchr(permissions, 'x')) { 557 mapping.permissions |= PF_X; 558 } 559 mapping.start_address = start; 560 mapping.end_address = stop; 561 mapping.offset = offset; 562 if (filename) { 563 mapping.filename = filename; 564 } 565 crashinfo->mappings[mapping.start_address] = mapping; 566 } 567 free(permissions); 568 free(filename); 569 } 570 if (verbose) { 571 fputs("\n\n\n", stderr); 572 } 573} 574 575static void 576ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 577 if (verbose) { 578 fputs("MD_LINUX_ENVIRON:\n", stderr); 579 char* env = new char[range.length()]; 580 memcpy(env, range.data(), range.length()); 581 int nul_count = 0; 582 for (char *ptr = env;;) { 583 ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env)); 584 if (!ptr) { 585 break; 586 } 587 if (ptr > env && ptr[-1] == '\n') { 588 if (++nul_count > 5) { 589 // Some versions of Chrome try to rewrite the process' command line 590 // in a way that causes the environment to be corrupted. Afterwards, 591 // part of the environment will contain the trailing bit of the 592 // command line. The rest of the environment will be filled with 593 // NUL bytes. 594 // We detect this corruption by counting the number of consecutive 595 // NUL bytes. Normally, we would not expect any consecutive NUL 596 // bytes. But we are conservative and only suppress printing of 597 // the environment if we see at least five consecutive NULs. 598 fputs("Environment has been corrupted; no data available", stderr); 599 goto env_corrupted; 600 } 601 } else { 602 nul_count = 0; 603 } 604 *ptr = '\n'; 605 } 606 fwrite(env, range.length(), 1, stderr); 607 env_corrupted: 608 delete[] env; 609 fputs("\n\n\n", stderr); 610 } 611} 612 613static void 614ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 615 // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value 616 // when dumping /proc/$x/maps 617 if (range.length() > 17) { 618 // The AUXV vector contains binary data, whereas the maps always begin 619 // with an 8+ digit hex address followed by a hyphen and another 8+ digit 620 // address. 621 char addresses[18]; 622 memcpy(addresses, range.data(), 17); 623 addresses[17] = '\000'; 624 if (strspn(addresses, "0123456789abcdef-") == 17) { 625 ParseMaps(crashinfo, range); 626 return; 627 } 628 } 629 630 crashinfo->auxv = range.data(); 631 crashinfo->auxv_length = range.length(); 632} 633 634static void 635ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) { 636 // The command line is supposed to use NUL bytes to separate arguments. 637 // As Chrome rewrites its own command line and (incorrectly) substitutes 638 // spaces, this is often not the case in our minidump files. 639 const char* cmdline = (const char*) range.data(); 640 if (verbose) { 641 fputs("MD_LINUX_CMD_LINE:\n", stderr); 642 unsigned i = 0; 643 for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { } 644 fputs("argv[0] = \"", stderr); 645 fwrite(cmdline, i, 1, stderr); 646 fputs("\"\n", stderr); 647 for (unsigned j = ++i, argc = 1; j < range.length(); ++j) { 648 if (!cmdline[j] || cmdline[j] == ' ') { 649 fprintf(stderr, "argv[%d] = \"", argc++); 650 fwrite(cmdline + i, j - i, 1, stderr); 651 fputs("\"\n", stderr); 652 i = j + 1; 653 } 654 } 655 fputs("\n\n", stderr); 656 } 657 658 const char *binary_name = cmdline; 659 for (size_t i = 0; i < range.length(); ++i) { 660 if (cmdline[i] == '/') { 661 binary_name = cmdline + i + 1; 662 } else if (cmdline[i] == 0 || cmdline[i] == ' ') { 663 static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; 664 static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; 665 memset(crashinfo->prps.pr_fname, 0, fname_len + 1); 666 memset(crashinfo->prps.pr_psargs, 0, args_len + 1); 667 unsigned len = cmdline + i - binary_name; 668 memcpy(crashinfo->prps.pr_fname, binary_name, 669 len > fname_len ? fname_len : len); 670 671 len = range.length() > args_len ? args_len : range.length(); 672 memcpy(crashinfo->prps.pr_psargs, cmdline, len); 673 for (unsigned j = 0; j < len; ++j) { 674 if (crashinfo->prps.pr_psargs[j] == 0) 675 crashinfo->prps.pr_psargs[j] = ' '; 676 } 677 break; 678 } 679 } 680} 681 682static void 683ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, 684 const MinidumpMemoryRange& full_file) { 685 const MDRawDebug* debug = range.GetData<MDRawDebug>(0); 686 if (!debug) { 687 return; 688 } 689 if (verbose) { 690 fprintf(stderr, 691 "MD_LINUX_DSO_DEBUG:\n" 692 "Version: %d\n" 693 "Number of DSOs: %d\n" 694 "Brk handler: %p\n" 695 "Dynamic loader at: %p\n" 696 "_DYNAMIC: %p\n", 697 debug->version, 698 debug->dso_count, 699 debug->brk, 700 debug->ldbase, 701 debug->dynamic); 702 } 703 crashinfo->debug = *debug; 704 if (range.length() > sizeof(MDRawDebug)) { 705 char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug); 706 crashinfo->dynamic_data.assign(dynamic_data, 707 range.length() - sizeof(MDRawDebug)); 708 } 709 if (debug->map != kInvalidMDRVA) { 710 for (unsigned int i = 0; i < debug->dso_count; ++i) { 711 const MDRawLinkMap* link_map = 712 full_file.GetArrayElement<MDRawLinkMap>(debug->map, i); 713 if (link_map) { 714 if (verbose) { 715 fprintf(stderr, 716 "#%03d: %p, %p, \"%s\"\n", 717 i, link_map->addr, link_map->ld, 718 full_file.GetAsciiMDString(link_map->name).c_str()); 719 } 720 crashinfo->link_map.push_back(*link_map); 721 } 722 } 723 } 724 if (verbose) { 725 fputs("\n\n", stderr); 726 } 727} 728 729static void 730ParseExceptionStream(CrashedProcess* crashinfo, 731 const MinidumpMemoryRange& range) { 732 const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0); 733 crashinfo->crashing_tid = exp->thread_id; 734 crashinfo->fatal_signal = (int) exp->exception_record.exception_code; 735} 736 737static bool 738WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { 739 struct prstatus pr; 740 memset(&pr, 0, sizeof(pr)); 741 742 pr.pr_info.si_signo = fatal_signal; 743 pr.pr_cursig = fatal_signal; 744 pr.pr_pid = thread.tid; 745 memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); 746 747 Nhdr nhdr; 748 memset(&nhdr, 0, sizeof(nhdr)); 749 nhdr.n_namesz = 5; 750 nhdr.n_descsz = sizeof(struct prstatus); 751 nhdr.n_type = NT_PRSTATUS; 752 if (!writea(1, &nhdr, sizeof(nhdr)) || 753 !writea(1, "CORE\0\0\0\0", 8) || 754 !writea(1, &pr, sizeof(struct prstatus))) { 755 return false; 756 } 757 758#if defined(__i386__) || defined(__x86_64__) 759 nhdr.n_descsz = sizeof(user_fpregs_struct); 760 nhdr.n_type = NT_FPREGSET; 761 if (!writea(1, &nhdr, sizeof(nhdr)) || 762 !writea(1, "CORE\0\0\0\0", 8) || 763 !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { 764 return false; 765 } 766#endif 767 768#if defined(__i386__) 769 nhdr.n_descsz = sizeof(user_fpxregs_struct); 770 nhdr.n_type = NT_PRXFPREG; 771 if (!writea(1, &nhdr, sizeof(nhdr)) || 772 !writea(1, "LINUX\0\0\0", 8) || 773 !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { 774 return false; 775 } 776#endif 777 778 return true; 779} 780 781static void 782ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range, 783 const MinidumpMemoryRange& full_file) { 784 if (verbose) { 785 fputs("MD_MODULE_LIST_STREAM:\n", stderr); 786 } 787 const uint32_t num_mappings = *range.GetData<uint32_t>(0); 788 for (unsigned i = 0; i < num_mappings; ++i) { 789 CrashedProcess::Mapping mapping; 790 const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>( 791 range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i)); 792 mapping.start_address = rawmodule->base_of_image; 793 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; 794 795 if (crashinfo->mappings.find(mapping.start_address) == 796 crashinfo->mappings.end()) { 797 // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as 798 // the former is a strict superset of the latter. 799 crashinfo->mappings[mapping.start_address] = mapping; 800 } 801 802 const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>( 803 full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize)); 804 char guid[40]; 805 sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 806 record->signature.data1, record->signature.data2, 807 record->signature.data3, 808 record->signature.data4[0], record->signature.data4[1], 809 record->signature.data4[2], record->signature.data4[3], 810 record->signature.data4[4], record->signature.data4[5], 811 record->signature.data4[6], record->signature.data4[7]); 812 std::string filename = 813 full_file.GetAsciiMDString(rawmodule->module_name_rva); 814 size_t slash = filename.find_last_of('/'); 815 std::string basename = slash == std::string::npos ? 816 filename : filename.substr(slash + 1); 817 if (strcmp(guid, "00000000-0000-0000-0000-000000000000")) { 818 std::string prefix; 819 if (!g_custom_so_basedir.empty()) 820 prefix = g_custom_so_basedir; 821 else 822 prefix = std::string("/var/lib/breakpad/") + guid + "-" + basename; 823 824 crashinfo->signatures[rawmodule->base_of_image] = prefix + basename; 825 } 826 827 if (verbose) { 828 fprintf(stderr, "0x%08llX-0x%08llX, ChkSum: 0x%08X, GUID: %s, \"%s\"\n", 829 (unsigned long long)rawmodule->base_of_image, 830 (unsigned long long)rawmodule->base_of_image + 831 rawmodule->size_of_image, 832 rawmodule->checksum, guid, filename.c_str()); 833 } 834 } 835 if (verbose) { 836 fputs("\n\n", stderr); 837 } 838} 839 840static void 841AddDataToMapping(CrashedProcess* crashinfo, const std::string& data, 842 uintptr_t addr) { 843 for (std::map<uint64_t, CrashedProcess::Mapping>::iterator 844 iter = crashinfo->mappings.begin(); 845 iter != crashinfo->mappings.end(); 846 ++iter) { 847 if (addr >= iter->second.start_address && 848 addr < iter->second.end_address) { 849 CrashedProcess::Mapping mapping = iter->second; 850 if ((addr & ~4095) != iter->second.start_address) { 851 // If there are memory pages in the mapping prior to where the 852 // data starts, truncate the existing mapping so that it ends with 853 // the page immediately preceding the data region. 854 iter->second.end_address = addr & ~4095; 855 if (!mapping.filename.empty()) { 856 // "mapping" is a copy of "iter->second". We are splitting the 857 // existing mapping into two separate ones when we write the data 858 // to the core file. The first one does not have any associated 859 // data in the core file, the second one is backed by data that is 860 // included with the core file. 861 // If this mapping wasn't supposed to be anonymous, then we also 862 // have to update the file offset upon splitting the mapping. 863 mapping.offset += iter->second.end_address - 864 iter->second.start_address; 865 } 866 } 867 // Create a new mapping that contains the data contents. We often 868 // limit the amount of data that is actually written to the core 869 // file. But it is OK if the mapping itself extends past the end of 870 // the data. 871 mapping.start_address = addr & ~4095; 872 mapping.data.assign(addr & 4095, 0).append(data); 873 mapping.data.append(-mapping.data.size() & 4095, 0); 874 crashinfo->mappings[mapping.start_address] = mapping; 875 return; 876 } 877 } 878 // Didn't find a suitable existing mapping for the data. Create a new one. 879 CrashedProcess::Mapping mapping; 880 mapping.permissions = PF_R | PF_W; 881 mapping.start_address = addr & ~4095; 882 mapping.end_address = 883 (addr + data.size() + 4095) & ~4095; 884 mapping.data.assign(addr & 4095, 0).append(data); 885 mapping.data.append(-mapping.data.size() & 4095, 0); 886 crashinfo->mappings[mapping.start_address] = mapping; 887} 888 889static void 890AugmentMappings(CrashedProcess* crashinfo, 891 const MinidumpMemoryRange& full_file) { 892 // For each thread, find the memory mapping that matches the thread's stack. 893 // Then adjust the mapping to include the stack dump. 894 for (unsigned i = 0; i < crashinfo->threads.size(); ++i) { 895 const CrashedProcess::Thread& thread = crashinfo->threads[i]; 896 AddDataToMapping(crashinfo, 897 std::string((char *)thread.stack, thread.stack_length), 898 thread.stack_addr); 899 } 900 901 // Create a new link map with information about DSOs. We move this map to 902 // the beginning of the address space, as this area should always be 903 // available. 904 static const uintptr_t start_addr = 4096; 905 std::string data; 906 struct r_debug debug = { 0 }; 907 debug.r_version = crashinfo->debug.version; 908 debug.r_brk = (ElfW(Addr))crashinfo->debug.brk; 909 debug.r_state = r_debug::RT_CONSISTENT; 910 debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase; 911 debug.r_map = crashinfo->debug.dso_count > 0 ? 912 (struct link_map*)(start_addr + sizeof(debug)) : 0; 913 data.append((char*)&debug, sizeof(debug)); 914 915 struct link_map* prev = 0; 916 for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin(); 917 iter != crashinfo->link_map.end(); 918 ++iter) { 919 struct link_map link_map = { 0 }; 920 link_map.l_addr = (ElfW(Addr))iter->addr; 921 link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map)); 922 link_map.l_ld = (ElfW(Dyn)*)iter->ld; 923 link_map.l_prev = prev; 924 prev = (struct link_map*)(start_addr + data.size()); 925 std::string filename = full_file.GetAsciiMDString(iter->name); 926 927 // Look up signature for this filename. If available, change filename 928 // to point to GUID, instead. 929 std::map<uintptr_t, std::string>::const_iterator guid = 930 crashinfo->signatures.find((uintptr_t)iter->addr); 931 if (guid != crashinfo->signatures.end()) { 932 filename = guid->second; 933 } 934 935 if (std::distance(iter, crashinfo->link_map.end()) == 1) { 936 link_map.l_next = 0; 937 } else { 938 link_map.l_next = (struct link_map*)(start_addr + data.size() + 939 sizeof(link_map) + 940 ((filename.size() + 8) & ~7)); 941 } 942 data.append((char*)&link_map, sizeof(link_map)); 943 data.append(filename); 944 data.append(8 - (filename.size() & 7), 0); 945 } 946 AddDataToMapping(crashinfo, data, start_addr); 947 948 // Map the page containing the _DYNAMIC array 949 if (!crashinfo->dynamic_data.empty()) { 950 // Make _DYNAMIC DT_DEBUG entry point to our link map 951 for (int i = 0;; ++i) { 952 ElfW(Dyn) dyn; 953 if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) { 954 no_dt_debug: 955 if (verbose) { 956 fprintf(stderr, "No DT_DEBUG entry found\n"); 957 } 958 return; 959 } 960 memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn), 961 sizeof(dyn)); 962 if (dyn.d_tag == DT_DEBUG) { 963 crashinfo->dynamic_data.replace(i*sizeof(dyn) + 964 offsetof(ElfW(Dyn), d_un.d_ptr), 965 sizeof(start_addr), 966 (char*)&start_addr, sizeof(start_addr)); 967 break; 968 } else if (dyn.d_tag == DT_NULL) { 969 goto no_dt_debug; 970 } 971 } 972 AddDataToMapping(crashinfo, crashinfo->dynamic_data, 973 (uintptr_t)crashinfo->debug.dynamic); 974 } 975} 976 977int 978main(int argc, char** argv) { 979 int argi = 1; 980 while (argi < argc && argv[argi][0] == '-') { 981 if (!strcmp(argv[argi], "-v")) { 982 verbose = true; 983 } else if (!strcmp(argv[argi], "--sobasedir")) { 984 argi++; 985 if (argi >= argc) { 986 fprintf(stderr, "--sobasedir expects an argument."); 987 return usage(argv[0]); 988 } 989 990 g_custom_so_basedir = argv[argi]; 991 } else { 992 return usage(argv[0]); 993 } 994 argi++; 995 } 996 997 if (argc != argi + 1) 998 return usage(argv[0]); 999 1000 MemoryMappedFile mapped_file(argv[argi]); 1001 if (!mapped_file.data()) { 1002 fprintf(stderr, "Failed to mmap dump file\n"); 1003 return 1; 1004 } 1005 1006 MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size()); 1007 1008 const MDRawHeader* header = dump.GetData<MDRawHeader>(0); 1009 1010 CrashedProcess crashinfo; 1011 1012 // Always check the system info first, as that allows us to tell whether 1013 // this is a minidump file that is compatible with our converter. 1014 bool ok = false; 1015 for (unsigned i = 0; i < header->stream_count; ++i) { 1016 const MDRawDirectory* dirent = 1017 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i); 1018 switch (dirent->stream_type) { 1019 case MD_SYSTEM_INFO_STREAM: 1020 ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump); 1021 ok = true; 1022 break; 1023 default: 1024 break; 1025 } 1026 } 1027 if (!ok) { 1028 fprintf(stderr, "Cannot determine input file format.\n"); 1029 _exit(1); 1030 } 1031 1032 for (unsigned i = 0; i < header->stream_count; ++i) { 1033 const MDRawDirectory* dirent = 1034 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i); 1035 switch (dirent->stream_type) { 1036 case MD_THREAD_LIST_STREAM: 1037 ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); 1038 break; 1039 case MD_LINUX_CPU_INFO: 1040 ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location)); 1041 break; 1042 case MD_LINUX_PROC_STATUS: 1043 ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location)); 1044 break; 1045 case MD_LINUX_LSB_RELEASE: 1046 ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location)); 1047 break; 1048 case MD_LINUX_ENVIRON: 1049 ParseEnvironment(&crashinfo, dump.Subrange(dirent->location)); 1050 break; 1051 case MD_LINUX_MAPS: 1052 ParseMaps(&crashinfo, dump.Subrange(dirent->location)); 1053 break; 1054 case MD_LINUX_AUXV: 1055 ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); 1056 break; 1057 case MD_LINUX_CMD_LINE: 1058 ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); 1059 break; 1060 case MD_LINUX_DSO_DEBUG: 1061 ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump); 1062 break; 1063 case MD_EXCEPTION_STREAM: 1064 ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); 1065 break; 1066 case MD_MODULE_LIST_STREAM: 1067 ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump); 1068 break; 1069 default: 1070 if (verbose) 1071 fprintf(stderr, "Skipping %x\n", dirent->stream_type); 1072 } 1073 } 1074 1075 AugmentMappings(&crashinfo, dump); 1076 1077 // Write the ELF header. The file will look like: 1078 // ELF header 1079 // Phdr for the PT_NOTE 1080 // Phdr for each of the thread stacks 1081 // PT_NOTE 1082 // each of the thread stacks 1083 Ehdr ehdr; 1084 memset(&ehdr, 0, sizeof(Ehdr)); 1085 ehdr.e_ident[0] = ELFMAG0; 1086 ehdr.e_ident[1] = ELFMAG1; 1087 ehdr.e_ident[2] = ELFMAG2; 1088 ehdr.e_ident[3] = ELFMAG3; 1089 ehdr.e_ident[4] = ELF_CLASS; 1090 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; 1091 ehdr.e_ident[6] = EV_CURRENT; 1092 ehdr.e_type = ET_CORE; 1093 ehdr.e_machine = ELF_ARCH; 1094 ehdr.e_version = EV_CURRENT; 1095 ehdr.e_phoff = sizeof(Ehdr); 1096 ehdr.e_ehsize = sizeof(Ehdr); 1097 ehdr.e_phentsize= sizeof(Phdr); 1098 ehdr.e_phnum = 1 + // PT_NOTE 1099 crashinfo.mappings.size(); // memory mappings 1100 ehdr.e_shentsize= sizeof(Shdr); 1101 if (!writea(1, &ehdr, sizeof(Ehdr))) 1102 return 1; 1103 1104 size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr); 1105 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + 1106 // sizeof(Nhdr) + 8 + sizeof(user) + 1107 sizeof(Nhdr) + 8 + crashinfo.auxv_length + 1108 crashinfo.threads.size() * ( 1109 (sizeof(Nhdr) + 8 + sizeof(prstatus)) 1110#if defined(__i386__) || defined(__x86_64__) 1111 + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) 1112#endif 1113#if defined(__i386__) 1114 + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct) 1115#endif 1116 ); 1117 1118 Phdr phdr; 1119 memset(&phdr, 0, sizeof(Phdr)); 1120 phdr.p_type = PT_NOTE; 1121 phdr.p_offset = offset; 1122 phdr.p_filesz = filesz; 1123 if (!writea(1, &phdr, sizeof(phdr))) 1124 return 1; 1125 1126 phdr.p_type = PT_LOAD; 1127 phdr.p_align = 4096; 1128 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); 1129 if (note_align == phdr.p_align) 1130 note_align = 0; 1131 offset += note_align; 1132 1133 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter = 1134 crashinfo.mappings.begin(); 1135 iter != crashinfo.mappings.end(); ++iter) { 1136 const CrashedProcess::Mapping& mapping = iter->second; 1137 if (mapping.permissions == 0xFFFFFFFF) { 1138 // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to 1139 // MD_LINUX_MAPS). It lacks some of the information that we would like 1140 // to include. 1141 phdr.p_flags = PF_R; 1142 } else { 1143 phdr.p_flags = mapping.permissions; 1144 } 1145 phdr.p_vaddr = mapping.start_address; 1146 phdr.p_memsz = mapping.end_address - mapping.start_address; 1147 if (mapping.data.size()) { 1148 offset += filesz; 1149 filesz = mapping.data.size(); 1150 phdr.p_filesz = mapping.data.size(); 1151 phdr.p_offset = offset; 1152 } else { 1153 phdr.p_filesz = 0; 1154 phdr.p_offset = 0; 1155 } 1156 if (!writea(1, &phdr, sizeof(phdr))) 1157 return 1; 1158 } 1159 1160 Nhdr nhdr; 1161 memset(&nhdr, 0, sizeof(nhdr)); 1162 nhdr.n_namesz = 5; 1163 nhdr.n_descsz = sizeof(prpsinfo); 1164 nhdr.n_type = NT_PRPSINFO; 1165 if (!writea(1, &nhdr, sizeof(nhdr)) || 1166 !writea(1, "CORE\0\0\0\0", 8) || 1167 !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { 1168 return 1; 1169 } 1170 1171 nhdr.n_descsz = crashinfo.auxv_length; 1172 nhdr.n_type = NT_AUXV; 1173 if (!writea(1, &nhdr, sizeof(nhdr)) || 1174 !writea(1, "CORE\0\0\0\0", 8) || 1175 !writea(1, crashinfo.auxv, crashinfo.auxv_length)) { 1176 return 1; 1177 } 1178 1179 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { 1180 if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { 1181 WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); 1182 break; 1183 } 1184 } 1185 1186 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { 1187 if (crashinfo.threads[i].tid != crashinfo.crashing_tid) 1188 WriteThread(crashinfo.threads[i], 0); 1189 } 1190 1191 if (note_align) { 1192 google_breakpad::scoped_array<char> scratch(new char[note_align]); 1193 memset(scratch.get(), 0, note_align); 1194 if (!writea(1, scratch.get(), note_align)) 1195 return 1; 1196 } 1197 1198 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter = 1199 crashinfo.mappings.begin(); 1200 iter != crashinfo.mappings.end(); ++iter) { 1201 const CrashedProcess::Mapping& mapping = iter->second; 1202 if (mapping.data.size()) { 1203 if (!writea(1, mapping.data.c_str(), mapping.data.size())) 1204 return 1; 1205 } 1206 } 1207 1208 return 0; 1209} 1210