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