minidump-2-core.cc
1 // Copyright 2009 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // Converts a minidump file to a core file which gdb can read. 30 // Large parts lifted from the userspace core dumper: 31 // http://code.google.com/p/google-coredumper/ 32 33 #ifdef HAVE_CONFIG_H 34 #include <config.h> // Must come first 35 #endif 36 37 #include <elf.h> 38 #include <errno.h> 39 #include <limits.h> 40 #include <link.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <sys/user.h> 45 #include <unistd.h> 46 47 #include <map> 48 #include <string> 49 #include <vector> 50 51 #include "common/linux/memory_mapped_file.h" 52 #include "common/minidump_type_helper.h" 53 #include "common/path_helper.h" 54 #include "common/scoped_ptr.h" 55 #include "common/using_std_string.h" 56 #include "google_breakpad/common/breakpad_types.h" 57 #include "google_breakpad/common/minidump_format.h" 58 #include "third_party/lss/linux_syscall_support.h" 59 #include "tools/linux/md2core/minidump_memory_range.h" 60 61 #if ULONG_MAX == 0xffffffffffffffff 62 #define ELF_CLASS ELFCLASS64 63 #else 64 #define ELF_CLASS ELFCLASS32 65 #endif 66 #define Ehdr ElfW(Ehdr) 67 #define Phdr ElfW(Phdr) 68 #define Shdr ElfW(Shdr) 69 #define Nhdr ElfW(Nhdr) 70 #define auxv_t ElfW(auxv_t) 71 72 73 #if defined(__x86_64__) 74 #define ELF_ARCH EM_X86_64 75 #elif defined(__i386__) 76 #define ELF_ARCH EM_386 77 #elif defined(__arm__) 78 #define ELF_ARCH EM_ARM 79 #elif defined(__mips__) 80 #define ELF_ARCH EM_MIPS 81 #elif defined(__aarch64__) 82 #define ELF_ARCH EM_AARCH64 83 #elif defined(__riscv) 84 #define ELF_ARCH EM_RISCV 85 #endif 86 87 #if defined(__arm__) 88 // GLibc/ARM and Android/ARM both use 'user_regs' for the structure type 89 // containing core registers, while they use 'user_regs_struct' on other 90 // architectures. This file-local typedef simplifies the source code. 91 typedef user_regs user_regs_struct; 92 #elif defined (__mips__) || defined(__riscv) 93 // This file-local typedef simplifies the source code. 94 typedef gregset_t user_regs_struct; 95 #endif 96 97 using google_breakpad::MDTypeHelper; 98 using google_breakpad::MemoryMappedFile; 99 using google_breakpad::MinidumpMemoryRange; 100 101 typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawDebug MDRawDebug; 102 typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawLinkMap MDRawLinkMap; 103 104 static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1); 105 106 struct Options { 107 string minidump_path; 108 bool verbose; 109 int out_fd; 110 bool use_filename; 111 bool inc_guid; 112 string so_basedir; 113 }; 114 115 static void 116 Usage(int argc, const char* argv[]) { 117 fprintf(stderr, 118 "Usage: %s [options] <minidump file>\n" 119 "\n" 120 "Convert a minidump file into a core file (often for use by gdb).\n" 121 "\n" 122 "The shared library list will by default have filenames as the runtime expects.\n" 123 "There are many flags to control the output names though to make it easier to\n" 124 "integrate with your debug environment (e.g. gdb).\n" 125 " Default: /lib64/libpthread.so.0\n" 126 " -f: /lib64/libpthread-2.19.so\n" 127 " -i: /lib64/<module id>-libpthread.so.0\n" 128 " -f -i: /lib64/<module id>-libpthread-2.19.so\n" 129 " -S /foo/: /foo/libpthread.so.0\n" 130 "\n" 131 "Options:\n" 132 " -v Enable verbose output\n" 133 " -o <file> Write coredump to specified file (otherwise use stdout).\n" 134 " -f Use the filename rather than the soname in the sharedlib list.\n" 135 " The soname is what the runtime system uses, but the filename is\n" 136 " how it's stored on disk.\n" 137 " -i Prefix sharedlib names with ID (when available). This makes it\n" 138 " easier to have a single directory full of symbols.\n" 139 " -S <dir> Set soname base directory. This will force all debug/symbol\n" 140 " lookups to be done in this directory rather than the filesystem\n" 141 " layout as it exists in the crashing image. This path should end\n" 142 " with a slash if it's a directory. e.g. /var/lib/breakpad/\n" 143 "", google_breakpad::BaseName(argv[0]).c_str()); 144 } 145 146 static void 147 SetupOptions(int argc, const char* argv[], Options* options) { 148 extern int optind; 149 int ch; 150 const char* output_file = NULL; 151 152 // Initialize the options struct as needed. 153 options->verbose = false; 154 options->use_filename = false; 155 options->inc_guid = false; 156 157 while ((ch = getopt(argc, (char * const*)argv, "fhio:S:v")) != -1) { 158 switch (ch) { 159 case 'h': 160 Usage(argc, argv); 161 exit(0); 162 case '?': 163 Usage(argc, argv); 164 exit(1); 165 166 case 'f': 167 options->use_filename = true; 168 break; 169 case 'i': 170 options->inc_guid = true; 171 break; 172 case 'o': 173 output_file = optarg; 174 break; 175 case 'S': 176 options->so_basedir = optarg; 177 break; 178 case 'v': 179 options->verbose = true; 180 break; 181 } 182 } 183 184 if ((argc - optind) != 1) { 185 fprintf(stderr, "%s: Missing minidump file\n", argv[0]); 186 Usage(argc, argv); 187 exit(1); 188 } 189 190 if (output_file == NULL || !strcmp(output_file, "-")) { 191 options->out_fd = STDOUT_FILENO; 192 } else { 193 options->out_fd = open(output_file, O_WRONLY|O_CREAT|O_TRUNC, 0664); 194 if (options->out_fd == -1) { 195 fprintf(stderr, "%s: could not open output %s: %s\n", argv[0], 196 output_file, strerror(errno)); 197 exit(1); 198 } 199 } 200 201 options->minidump_path = argv[optind]; 202 } 203 204 // Write all of the given buffer, handling short writes and EINTR. Return true 205 // iff successful. 206 static bool 207 writea(int fd, const void* idata, size_t length) { 208 const uint8_t* data = (const uint8_t*) idata; 209 210 size_t done = 0; 211 while (done < length) { 212 ssize_t r; 213 do { 214 r = write(fd, data + done, length - done); 215 } while (r == -1 && errno == EINTR); 216 217 if (r < 1) 218 return false; 219 done += r; 220 } 221 222 return true; 223 } 224 225 /* Dynamically determines the byte sex of the system. Returns non-zero 226 * for big-endian machines. 227 */ 228 static inline int sex() { 229 int probe = 1; 230 return !*(char*)&probe; 231 } 232 233 typedef struct elf_timeval { /* Time value with microsecond resolution */ 234 long tv_sec; /* Seconds */ 235 long tv_usec; /* Microseconds */ 236 } elf_timeval; 237 238 typedef struct _elf_siginfo { /* Information about signal (unused) */ 239 int32_t si_signo; /* Signal number */ 240 int32_t si_code; /* Extra code */ 241 int32_t si_errno; /* Errno */ 242 } _elf_siginfo; 243 244 typedef struct prstatus { /* Information about thread; includes CPU reg*/ 245 _elf_siginfo pr_info; /* Info associated with signal */ 246 uint16_t pr_cursig; /* Current signal */ 247 unsigned long pr_sigpend; /* Set of pending signals */ 248 unsigned long pr_sighold; /* Set of held signals */ 249 pid_t pr_pid; /* Process ID */ 250 pid_t pr_ppid; /* Parent's process ID */ 251 pid_t pr_pgrp; /* Group ID */ 252 pid_t pr_sid; /* Session ID */ 253 elf_timeval pr_utime; /* User time */ 254 elf_timeval pr_stime; /* System time */ 255 elf_timeval pr_cutime; /* Cumulative user time */ 256 elf_timeval pr_cstime; /* Cumulative system time */ 257 user_regs_struct pr_reg; /* CPU registers */ 258 uint32_t pr_fpvalid; /* True if math co-processor being used */ 259 } prstatus; 260 261 typedef struct prpsinfo { /* Information about process */ 262 unsigned char pr_state; /* Numeric process state */ 263 char pr_sname; /* Char for pr_state */ 264 unsigned char pr_zomb; /* Zombie */ 265 signed char pr_nice; /* Nice val */ 266 unsigned long pr_flag; /* Flags */ 267 #if defined(__x86_64__) || defined(__mips__) || defined(__riscv) 268 uint32_t pr_uid; /* User ID */ 269 uint32_t pr_gid; /* Group ID */ 270 #else 271 uint16_t pr_uid; /* User ID */ 272 uint16_t pr_gid; /* Group ID */ 273 #endif 274 pid_t pr_pid; /* Process ID */ 275 pid_t pr_ppid; /* Parent's process ID */ 276 pid_t pr_pgrp; /* Group ID */ 277 pid_t pr_sid; /* Session ID */ 278 char pr_fname[16]; /* Filename of executable */ 279 char pr_psargs[80]; /* Initial part of arg list */ 280 } prpsinfo; 281 282 // We parse the minidump file and keep the parsed information in this structure 283 struct CrashedProcess { 284 CrashedProcess() 285 : exception{-1}, 286 auxv(NULL), 287 auxv_length(0) { 288 memset(&prps, 0, sizeof(prps)); 289 prps.pr_sname = 'R'; 290 memset(&debug, 0, sizeof(debug)); 291 } 292 293 struct Mapping { 294 Mapping() 295 : permissions(0xFFFFFFFF), 296 start_address(0), 297 end_address(0), 298 offset(0) { 299 } 300 301 uint32_t permissions; 302 uint64_t start_address, end_address, offset; 303 // The name we write out to the core. 304 string filename; 305 string data; 306 }; 307 std::map<uint64_t, Mapping> mappings; 308 309 int fatal_signal; 310 311 struct Thread { 312 pid_t tid; 313 #if defined(__mips__) || defined(__riscv) 314 mcontext_t mcontext; 315 #else 316 user_regs_struct regs; 317 #endif 318 #if defined(__i386__) || defined(__x86_64__) 319 user_fpregs_struct fpregs; 320 #endif 321 #if defined(__i386__) 322 user_fpxregs_struct fpxregs; 323 #endif 324 #if defined(__aarch64__) 325 user_fpsimd_struct fpregs; 326 #endif 327 uintptr_t stack_addr; 328 const uint8_t* stack; 329 size_t stack_length; 330 }; 331 std::vector<Thread> threads; 332 Thread exception; 333 334 const uint8_t* auxv; 335 size_t auxv_length; 336 337 prpsinfo prps; 338 339 // The GUID/filename from MD_MODULE_LIST_STREAM entries. 340 // We gather them for merging later on into the list of maps. 341 struct Signature { 342 char guid[40]; 343 string filename; 344 }; 345 std::map<uintptr_t, Signature> signatures; 346 347 string dynamic_data; 348 MDRawDebug debug; 349 std::vector<MDRawLinkMap> link_map; 350 }; 351 352 #if defined(__i386__) 353 static uint32_t 354 U32(const uint8_t* data) { 355 uint32_t v; 356 memcpy(&v, data, sizeof(v)); 357 return v; 358 } 359 360 static uint16_t 361 U16(const uint8_t* data) { 362 uint16_t v; 363 memcpy(&v, data, sizeof(v)); 364 return v; 365 } 366 367 static void 368 ParseThreadRegisters(CrashedProcess::Thread* thread, 369 const MinidumpMemoryRange& range) { 370 const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0); 371 372 thread->regs.ebx = rawregs->ebx; 373 thread->regs.ecx = rawregs->ecx; 374 thread->regs.edx = rawregs->edx; 375 thread->regs.esi = rawregs->esi; 376 thread->regs.edi = rawregs->edi; 377 thread->regs.ebp = rawregs->ebp; 378 thread->regs.eax = rawregs->eax; 379 thread->regs.xds = rawregs->ds; 380 thread->regs.xes = rawregs->es; 381 thread->regs.xfs = rawregs->fs; 382 thread->regs.xgs = rawregs->gs; 383 thread->regs.orig_eax = rawregs->eax; 384 thread->regs.eip = rawregs->eip; 385 thread->regs.xcs = rawregs->cs; 386 thread->regs.eflags = rawregs->eflags; 387 thread->regs.esp = rawregs->esp; 388 thread->regs.xss = rawregs->ss; 389 390 thread->fpregs.cwd = rawregs->float_save.control_word; 391 thread->fpregs.swd = rawregs->float_save.status_word; 392 thread->fpregs.twd = rawregs->float_save.tag_word; 393 thread->fpregs.fip = rawregs->float_save.error_offset; 394 thread->fpregs.fcs = rawregs->float_save.error_selector; 395 thread->fpregs.foo = rawregs->float_save.data_offset; 396 thread->fpregs.fos = rawregs->float_save.data_selector; 397 memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, 398 10 * 8); 399 400 thread->fpxregs.cwd = rawregs->float_save.control_word; 401 thread->fpxregs.swd = rawregs->float_save.status_word; 402 thread->fpxregs.twd = rawregs->float_save.tag_word; 403 thread->fpxregs.fop = U16(rawregs->extended_registers + 6); 404 thread->fpxregs.fip = U16(rawregs->extended_registers + 8); 405 thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); 406 thread->fpxregs.foo = U16(rawregs->extended_registers + 16); 407 thread->fpxregs.fos = U16(rawregs->extended_registers + 20); 408 thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); 409 memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); 410 memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); 411 } 412 #elif defined(__x86_64__) 413 static void 414 ParseThreadRegisters(CrashedProcess::Thread* thread, 415 const MinidumpMemoryRange& range) { 416 const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0); 417 418 thread->regs.r15 = rawregs->r15; 419 thread->regs.r14 = rawregs->r14; 420 thread->regs.r13 = rawregs->r13; 421 thread->regs.r12 = rawregs->r12; 422 thread->regs.rbp = rawregs->rbp; 423 thread->regs.rbx = rawregs->rbx; 424 thread->regs.r11 = rawregs->r11; 425 thread->regs.r10 = rawregs->r10; 426 thread->regs.r9 = rawregs->r9; 427 thread->regs.r8 = rawregs->r8; 428 thread->regs.rax = rawregs->rax; 429 thread->regs.rcx = rawregs->rcx; 430 thread->regs.rdx = rawregs->rdx; 431 thread->regs.rsi = rawregs->rsi; 432 thread->regs.rdi = rawregs->rdi; 433 thread->regs.orig_rax = rawregs->rax; 434 thread->regs.rip = rawregs->rip; 435 thread->regs.cs = rawregs->cs; 436 thread->regs.eflags = rawregs->eflags; 437 thread->regs.rsp = rawregs->rsp; 438 thread->regs.ss = rawregs->ss; 439 thread->regs.fs_base = 0; 440 thread->regs.gs_base = 0; 441 thread->regs.ds = rawregs->ds; 442 thread->regs.es = rawregs->es; 443 thread->regs.fs = rawregs->fs; 444 thread->regs.gs = rawregs->gs; 445 446 thread->fpregs.cwd = rawregs->flt_save.control_word; 447 thread->fpregs.swd = rawregs->flt_save.status_word; 448 thread->fpregs.ftw = rawregs->flt_save.tag_word; 449 thread->fpregs.fop = rawregs->flt_save.error_opcode; 450 thread->fpregs.rip = rawregs->flt_save.error_offset; 451 thread->fpregs.rdp = rawregs->flt_save.data_offset; 452 thread->fpregs.mxcsr = rawregs->flt_save.mx_csr; 453 thread->fpregs.mxcr_mask = rawregs->flt_save.mx_csr_mask; 454 memcpy(thread->fpregs.st_space, rawregs->flt_save.float_registers, 8 * 16); 455 memcpy(thread->fpregs.xmm_space, rawregs->flt_save.xmm_registers, 16 * 16); 456 } 457 #elif defined(__arm__) 458 static void 459 ParseThreadRegisters(CrashedProcess::Thread* thread, 460 const MinidumpMemoryRange& range) { 461 const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0); 462 463 thread->regs.uregs[0] = rawregs->iregs[0]; 464 thread->regs.uregs[1] = rawregs->iregs[1]; 465 thread->regs.uregs[2] = rawregs->iregs[2]; 466 thread->regs.uregs[3] = rawregs->iregs[3]; 467 thread->regs.uregs[4] = rawregs->iregs[4]; 468 thread->regs.uregs[5] = rawregs->iregs[5]; 469 thread->regs.uregs[6] = rawregs->iregs[6]; 470 thread->regs.uregs[7] = rawregs->iregs[7]; 471 thread->regs.uregs[8] = rawregs->iregs[8]; 472 thread->regs.uregs[9] = rawregs->iregs[9]; 473 thread->regs.uregs[10] = rawregs->iregs[10]; 474 thread->regs.uregs[11] = rawregs->iregs[11]; 475 thread->regs.uregs[12] = rawregs->iregs[12]; 476 thread->regs.uregs[13] = rawregs->iregs[13]; 477 thread->regs.uregs[14] = rawregs->iregs[14]; 478 thread->regs.uregs[15] = rawregs->iregs[15]; 479 480 thread->regs.uregs[16] = rawregs->cpsr; 481 thread->regs.uregs[17] = 0; // what is ORIG_r0 exactly? 482 } 483 #elif defined(__aarch64__) 484 static void 485 ParseThreadRegisters(CrashedProcess::Thread* thread, 486 const MinidumpMemoryRange& range) { 487 #define COPY_REGS(rawregs) \ 488 do { \ 489 for (int i = 0; i < 31; ++i) \ 490 thread->regs.regs[i] = rawregs->iregs[i]; \ 491 thread->regs.sp = rawregs->iregs[MD_CONTEXT_ARM64_REG_SP]; \ 492 thread->regs.pc = rawregs->iregs[MD_CONTEXT_ARM64_REG_PC]; \ 493 thread->regs.pstate = rawregs->cpsr; \ 494 \ 495 memcpy(thread->fpregs.vregs, rawregs->float_save.regs, 8 * 32); \ 496 thread->fpregs.fpsr = rawregs->float_save.fpsr; \ 497 thread->fpregs.fpcr = rawregs->float_save.fpcr; \ 498 } while (false) 499 500 if (range.length() == sizeof(MDRawContextARM64_Old)) { 501 const MDRawContextARM64_Old* rawregs = 502 range.GetData<MDRawContextARM64_Old>(0); 503 COPY_REGS(rawregs); 504 } else { 505 const MDRawContextARM64* rawregs = range.GetData<MDRawContextARM64>(0); 506 COPY_REGS(rawregs); 507 } 508 #undef COPY_REGS 509 } 510 #elif defined(__mips__) 511 static void 512 ParseThreadRegisters(CrashedProcess::Thread* thread, 513 const MinidumpMemoryRange& range) { 514 const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0); 515 516 for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) 517 thread->mcontext.gregs[i] = rawregs->iregs[i]; 518 519 thread->mcontext.pc = rawregs->epc; 520 521 thread->mcontext.mdlo = rawregs->mdlo; 522 thread->mcontext.mdhi = rawregs->mdhi; 523 524 thread->mcontext.hi1 = rawregs->hi[0]; 525 thread->mcontext.lo1 = rawregs->lo[0]; 526 thread->mcontext.hi2 = rawregs->hi[1]; 527 thread->mcontext.lo2 = rawregs->lo[1]; 528 thread->mcontext.hi3 = rawregs->hi[2]; 529 thread->mcontext.lo3 = rawregs->lo[2]; 530 531 for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) { 532 thread->mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs = 533 rawregs->float_save.regs[i]; 534 } 535 536 thread->mcontext.fpc_csr = rawregs->float_save.fpcsr; 537 #if _MIPS_SIM == _ABIO32 538 thread->mcontext.fpc_eir = rawregs->float_save.fir; 539 #endif 540 } 541 #elif defined(__riscv) 542 static void 543 ParseThreadRegisters(CrashedProcess::Thread* thread, 544 const MinidumpMemoryRange& range) { 545 # if __riscv_xlen == 32 546 const MDRawContextRISCV* rawregs = range.GetData<MDRawContextRISCV>(0); 547 # elif __riscv_xlen == 64 548 const MDRawContextRISCV64* rawregs = range.GetData<MDRawContextRISCV64>(0); 549 # else 550 # error "Unexpected __riscv_xlen" 551 # endif 552 553 thread->mcontext.__gregs[0] = rawregs->pc; 554 thread->mcontext.__gregs[1] = rawregs->ra; 555 thread->mcontext.__gregs[2] = rawregs->sp; 556 thread->mcontext.__gregs[3] = rawregs->gp; 557 thread->mcontext.__gregs[4] = rawregs->tp; 558 thread->mcontext.__gregs[5] = rawregs->t0; 559 thread->mcontext.__gregs[6] = rawregs->t1; 560 thread->mcontext.__gregs[7] = rawregs->t2; 561 thread->mcontext.__gregs[8] = rawregs->s0; 562 thread->mcontext.__gregs[9] = rawregs->s1; 563 thread->mcontext.__gregs[10] = rawregs->a0; 564 thread->mcontext.__gregs[11] = rawregs->a1; 565 thread->mcontext.__gregs[12] = rawregs->a2; 566 thread->mcontext.__gregs[13] = rawregs->a3; 567 thread->mcontext.__gregs[14] = rawregs->a4; 568 thread->mcontext.__gregs[15] = rawregs->a5; 569 thread->mcontext.__gregs[16] = rawregs->a6; 570 thread->mcontext.__gregs[17] = rawregs->a7; 571 thread->mcontext.__gregs[18] = rawregs->s2; 572 thread->mcontext.__gregs[19] = rawregs->s3; 573 thread->mcontext.__gregs[20] = rawregs->s4; 574 thread->mcontext.__gregs[21] = rawregs->s5; 575 thread->mcontext.__gregs[22] = rawregs->s6; 576 thread->mcontext.__gregs[23] = rawregs->s7; 577 thread->mcontext.__gregs[24] = rawregs->s8; 578 thread->mcontext.__gregs[25] = rawregs->s9; 579 thread->mcontext.__gregs[26] = rawregs->s10; 580 thread->mcontext.__gregs[27] = rawregs->s11; 581 thread->mcontext.__gregs[28] = rawregs->t3; 582 thread->mcontext.__gregs[29] = rawregs->t4; 583 thread->mcontext.__gregs[30] = rawregs->t5; 584 thread->mcontext.__gregs[31] = rawregs->t6; 585 586 // Breakpad only supports RISCV32 with 32 bit floating point. 587 // Breakpad only supports RISCV64 with 64 bit floating point. 588 #if __riscv_xlen == 32 589 for (int i = 0; i < MD_CONTEXT_RISCV_FPR_COUNT; ++i) { 590 thread->mcontext.__fpregs.__f.__f[i] = rawregs->fpregs[i]; 591 } 592 thread->mcontext.__fpregs.__f.__fcsr = rawregs->fcsr; 593 #elif __riscv_xlen == 64 594 for (int i = 0; i < MD_CONTEXT_RISCV_FPR_COUNT; ++i) { 595 thread->mcontext.__fpregs.__d.__f[i] = rawregs->fpregs[i]; 596 } 597 thread->mcontext.__fpregs.__d.__fcsr = rawregs->fcsr; 598 #else 599 #error "Unexpected __riscv_xlen" 600 #endif 601 } 602 #else 603 #error "This code has not been ported to your platform yet" 604 #endif 605 606 static void 607 ParseThreadList(const Options& options, CrashedProcess* crashinfo, 608 const MinidumpMemoryRange& range, 609 const MinidumpMemoryRange& full_file) { 610 const uint32_t num_threads = *range.GetData<uint32_t>(0); 611 if (options.verbose) { 612 fprintf(stderr, 613 "MD_THREAD_LIST_STREAM:\n" 614 "Found %d threads\n" 615 "\n\n", 616 num_threads); 617 } 618 for (unsigned i = 0; i < num_threads; ++i) { 619 CrashedProcess::Thread thread; 620 memset(&thread, 0, sizeof(thread)); 621 const MDRawThread* rawthread = 622 range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i); 623 thread.tid = rawthread->thread_id; 624 thread.stack_addr = rawthread->stack.start_of_memory_range; 625 MinidumpMemoryRange stack_range = 626 full_file.Subrange(rawthread->stack.memory); 627 thread.stack = stack_range.data(); 628 thread.stack_length = rawthread->stack.memory.data_size; 629 630 ParseThreadRegisters(&thread, 631 full_file.Subrange(rawthread->thread_context)); 632 633 crashinfo->threads.push_back(thread); 634 } 635 } 636 637 static void 638 ParseSystemInfo(const Options& options, CrashedProcess* crashinfo, 639 const MinidumpMemoryRange& range, 640 const MinidumpMemoryRange& full_file) { 641 const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0); 642 if (!sysinfo) { 643 fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n"); 644 exit(1); 645 } 646 #if defined(__i386__) 647 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) { 648 fprintf(stderr, 649 "This version of minidump-2-core only supports x86 (32bit)%s.\n", 650 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ? 651 ",\nbut the minidump file is from a 64bit machine" : ""); 652 exit(1); 653 } 654 #elif defined(__x86_64__) 655 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) { 656 fprintf(stderr, 657 "This version of minidump-2-core only supports x86 (64bit)%s.\n", 658 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ? 659 ",\nbut the minidump file is from a 32bit machine" : ""); 660 exit(1); 661 } 662 #elif defined(__arm__) 663 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) { 664 fprintf(stderr, 665 "This version of minidump-2-core only supports ARM (32bit).\n"); 666 exit(1); 667 } 668 #elif defined(__aarch64__) 669 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM64_OLD && 670 sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM64) { 671 fprintf(stderr, 672 "This version of minidump-2-core only supports ARM (64bit).\n"); 673 exit(1); 674 } 675 #elif defined(__mips__) 676 # if _MIPS_SIM == _ABIO32 677 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) { 678 fprintf(stderr, 679 "This version of minidump-2-core only supports mips o32 (32bit).\n"); 680 exit(1); 681 } 682 # elif _MIPS_SIM == _ABI64 683 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS64) { 684 fprintf(stderr, 685 "This version of minidump-2-core only supports mips n64 (64bit).\n"); 686 exit(1); 687 } 688 # else 689 # error "This mips ABI is currently not supported (n32)" 690 # endif 691 #elif defined(__riscv) 692 # if __riscv_xlen == 32 693 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_RISCV) { 694 fprintf(stderr, 695 "This version of minidump-2-core only supports RISCV.\n"); 696 exit(1); 697 } 698 # elif __riscv_xlen == 64 699 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_RISCV64) { 700 fprintf(stderr, 701 "This version of minidump-2-core only supports RISCV64.\n"); 702 exit(1); 703 } 704 # else 705 # error "Unexpected __riscv_xlen" 706 # endif 707 #else 708 #error "This code has not been ported to your platform yet" 709 #endif 710 if (sysinfo->platform_id != MD_OS_LINUX && 711 sysinfo->platform_id != MD_OS_NACL) { 712 fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n"); 713 exit(1); 714 } 715 716 if (options.verbose) { 717 fprintf(stderr, 718 "MD_SYSTEM_INFO_STREAM:\n" 719 "Architecture: %s\n" 720 "Number of processors: %d\n" 721 "Processor level: %d\n" 722 "Processor model: %d\n" 723 "Processor stepping: %d\n", 724 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 725 ? "i386" 726 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 727 ? "x86-64" 728 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM 729 ? "ARM" 730 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS 731 ? "MIPS" 732 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS64 733 ? "MIPS64" 734 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_RISCV 735 ? "RISCV" 736 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_RISCV64 737 ? "RISCV64" 738 : "???", 739 sysinfo->number_of_processors, 740 sysinfo->processor_level, 741 sysinfo->processor_revision >> 8, 742 sysinfo->processor_revision & 0xFF); 743 if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 || 744 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) { 745 fputs("Vendor id: ", stderr); 746 const char *nul = 747 (const char*)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0, 748 sizeof(sysinfo->cpu.x86_cpu_info.vendor_id)); 749 fwrite(sysinfo->cpu.x86_cpu_info.vendor_id, 750 nul ? nul - (const char*)&sysinfo->cpu.x86_cpu_info.vendor_id[0] 751 : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr); 752 fputs("\n", stderr); 753 } 754 fprintf(stderr, "OS: %s\n", 755 full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str()); 756 fputs("\n\n", stderr); 757 } 758 } 759 760 static void 761 ParseCPUInfo(const Options& options, CrashedProcess* crashinfo, 762 const MinidumpMemoryRange& range) { 763 if (options.verbose) { 764 fputs("MD_LINUX_CPU_INFO:\n", stderr); 765 fwrite(range.data(), range.length(), 1, stderr); 766 fputs("\n\n\n", stderr); 767 } 768 } 769 770 static void 771 ParseProcessStatus(const Options& options, CrashedProcess* crashinfo, 772 const MinidumpMemoryRange& range) { 773 if (options.verbose) { 774 fputs("MD_LINUX_PROC_STATUS:\n", stderr); 775 fwrite(range.data(), range.length(), 1, stderr); 776 fputs("\n\n", stderr); 777 } 778 } 779 780 static void 781 ParseLSBRelease(const Options& options, CrashedProcess* crashinfo, 782 const MinidumpMemoryRange& range) { 783 if (options.verbose) { 784 fputs("MD_LINUX_LSB_RELEASE:\n", stderr); 785 fwrite(range.data(), range.length(), 1, stderr); 786 fputs("\n\n", stderr); 787 } 788 } 789 790 static void 791 ParseMaps(const Options& options, CrashedProcess* crashinfo, 792 const MinidumpMemoryRange& range) { 793 if (options.verbose) { 794 fputs("MD_LINUX_MAPS:\n", stderr); 795 fwrite(range.data(), range.length(), 1, stderr); 796 } 797 for (const uint8_t* ptr = range.data(); 798 ptr < range.data() + range.length();) { 799 const uint8_t* eol = (uint8_t*)memchr(ptr, '\n', 800 range.data() + range.length() - ptr); 801 string line((const char*)ptr, 802 eol ? eol - ptr : range.data() + range.length() - ptr); 803 ptr = eol ? eol + 1 : range.data() + range.length(); 804 unsigned long long start, stop, offset; 805 char* permissions = NULL; 806 char* filename = NULL; 807 sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms", 808 &start, &stop, &permissions, &offset, &filename); 809 if (filename && *filename == '/') { 810 CrashedProcess::Mapping mapping; 811 mapping.permissions = 0; 812 if (strchr(permissions, 'r')) { 813 mapping.permissions |= PF_R; 814 } 815 if (strchr(permissions, 'w')) { 816 mapping.permissions |= PF_W; 817 } 818 if (strchr(permissions, 'x')) { 819 mapping.permissions |= PF_X; 820 } 821 mapping.start_address = start; 822 mapping.end_address = stop; 823 mapping.offset = offset; 824 if (filename) { 825 mapping.filename = filename; 826 } 827 crashinfo->mappings[mapping.start_address] = mapping; 828 } 829 free(permissions); 830 free(filename); 831 } 832 if (options.verbose) { 833 fputs("\n\n\n", stderr); 834 } 835 } 836 837 static void 838 ParseEnvironment(const Options& options, CrashedProcess* crashinfo, 839 const MinidumpMemoryRange& range) { 840 if (options.verbose) { 841 fputs("MD_LINUX_ENVIRON:\n", stderr); 842 char* env = new char[range.length()]; 843 memcpy(env, range.data(), range.length()); 844 int nul_count = 0; 845 for (char *ptr = env;;) { 846 ptr = (char*)memchr(ptr, '\000', range.length() - (ptr - env)); 847 if (!ptr) { 848 break; 849 } 850 if (ptr > env && ptr[-1] == '\n') { 851 if (++nul_count > 5) { 852 // Some versions of Chrome try to rewrite the process' command line 853 // in a way that causes the environment to be corrupted. Afterwards, 854 // part of the environment will contain the trailing bit of the 855 // command line. The rest of the environment will be filled with 856 // NUL bytes. 857 // We detect this corruption by counting the number of consecutive 858 // NUL bytes. Normally, we would not expect any consecutive NUL 859 // bytes. But we are conservative and only suppress printing of 860 // the environment if we see at least five consecutive NULs. 861 fputs("Environment has been corrupted; no data available", stderr); 862 goto env_corrupted; 863 } 864 } else { 865 nul_count = 0; 866 } 867 *ptr = '\n'; 868 } 869 fwrite(env, range.length(), 1, stderr); 870 env_corrupted: 871 delete[] env; 872 fputs("\n\n\n", stderr); 873 } 874 } 875 876 static void 877 ParseAuxVector(const Options& options, CrashedProcess* crashinfo, 878 const MinidumpMemoryRange& range) { 879 // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value 880 // when dumping /proc/$x/maps 881 if (range.length() > 17) { 882 // The AUXV vector contains binary data, whereas the maps always begin 883 // with an 8+ digit hex address followed by a hyphen and another 8+ digit 884 // address. 885 char addresses[18]; 886 memcpy(addresses, range.data(), 17); 887 addresses[17] = '\000'; 888 if (strspn(addresses, "0123456789abcdef-") == 17) { 889 ParseMaps(options, crashinfo, range); 890 return; 891 } 892 } 893 894 crashinfo->auxv = range.data(); 895 crashinfo->auxv_length = range.length(); 896 } 897 898 static void 899 ParseCmdLine(const Options& options, CrashedProcess* crashinfo, 900 const MinidumpMemoryRange& range) { 901 // The command line is supposed to use NUL bytes to separate arguments. 902 // As Chrome rewrites its own command line and (incorrectly) substitutes 903 // spaces, this is often not the case in our minidump files. 904 const char* cmdline = (const char*) range.data(); 905 if (options.verbose) { 906 fputs("MD_LINUX_CMD_LINE:\n", stderr); 907 unsigned i = 0; 908 for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { } 909 fputs("argv[0] = \"", stderr); 910 fwrite(cmdline, i, 1, stderr); 911 fputs("\"\n", stderr); 912 for (unsigned j = ++i, argc = 1; j < range.length(); ++j) { 913 if (!cmdline[j] || cmdline[j] == ' ') { 914 fprintf(stderr, "argv[%d] = \"", argc++); 915 fwrite(cmdline + i, j - i, 1, stderr); 916 fputs("\"\n", stderr); 917 i = j + 1; 918 } 919 } 920 fputs("\n\n", stderr); 921 } 922 923 const char *binary_name = cmdline; 924 for (size_t i = 0; i < range.length(); ++i) { 925 if (cmdline[i] == '/') { 926 binary_name = cmdline + i + 1; 927 } else if (cmdline[i] == 0 || cmdline[i] == ' ') { 928 static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; 929 static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; 930 memset(crashinfo->prps.pr_fname, 0, fname_len + 1); 931 memset(crashinfo->prps.pr_psargs, 0, args_len + 1); 932 unsigned len = cmdline + i - binary_name; 933 memcpy(crashinfo->prps.pr_fname, binary_name, 934 len > fname_len ? fname_len : len); 935 936 len = range.length() > args_len ? args_len : range.length(); 937 memcpy(crashinfo->prps.pr_psargs, cmdline, len); 938 for (unsigned j = 0; j < len; ++j) { 939 if (crashinfo->prps.pr_psargs[j] == 0) 940 crashinfo->prps.pr_psargs[j] = ' '; 941 } 942 break; 943 } 944 } 945 } 946 947 static void 948 ParseDSODebugInfo(const Options& options, CrashedProcess* crashinfo, 949 const MinidumpMemoryRange& range, 950 const MinidumpMemoryRange& full_file) { 951 const MDRawDebug* debug = range.GetData<MDRawDebug>(0); 952 if (!debug) { 953 return; 954 } 955 if (options.verbose) { 956 fprintf(stderr, 957 "MD_LINUX_DSO_DEBUG:\n" 958 "Version: %d\n" 959 "Number of DSOs: %d\n" 960 "Brk handler: 0x%" PRIx64 "\n" 961 "Dynamic loader at: 0x%" PRIx64 "\n" 962 "_DYNAMIC: 0x%" PRIx64 "\n", 963 debug->version, 964 debug->dso_count, 965 static_cast<uint64_t>(debug->brk), 966 static_cast<uint64_t>(debug->ldbase), 967 static_cast<uint64_t>(debug->dynamic)); 968 } 969 crashinfo->debug = *debug; 970 if (range.length() > sizeof(MDRawDebug)) { 971 char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug); 972 crashinfo->dynamic_data.assign(dynamic_data, 973 range.length() - sizeof(MDRawDebug)); 974 } 975 if (debug->map != kInvalidMDRVA) { 976 for (unsigned int i = 0; i < debug->dso_count; ++i) { 977 const MDRawLinkMap* link_map = 978 full_file.GetArrayElement<MDRawLinkMap>(debug->map, i); 979 if (link_map) { 980 if (options.verbose) { 981 fprintf(stderr, 982 "#%03d: %" PRIx64 ", %" PRIx64 ", \"%s\"\n", 983 i, static_cast<uint64_t>(link_map->addr), 984 static_cast<uint64_t>(link_map->ld), 985 full_file.GetAsciiMDString(link_map->name).c_str()); 986 } 987 crashinfo->link_map.push_back(*link_map); 988 } 989 } 990 } 991 if (options.verbose) { 992 fputs("\n\n", stderr); 993 } 994 } 995 996 static void 997 ParseExceptionStream(const Options& options, CrashedProcess* crashinfo, 998 const MinidumpMemoryRange& range, 999 const MinidumpMemoryRange& full_file) { 1000 const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0); 1001 if (!exp) { 1002 return; 1003 } 1004 if (options.verbose) { 1005 fprintf(stderr, 1006 "MD_EXCEPTION_STREAM:\n" 1007 "Found exception thread %" PRIu32 " \n" 1008 "\n\n", 1009 exp->thread_id); 1010 } 1011 crashinfo->fatal_signal = (int) exp->exception_record.exception_code; 1012 crashinfo->exception = {}; 1013 crashinfo->exception.tid = exp->thread_id; 1014 // crashinfo->threads[].tid == crashinfo->exception.tid provides the stack. 1015 ParseThreadRegisters(&crashinfo->exception, 1016 full_file.Subrange(exp->thread_context)); 1017 } 1018 1019 static bool 1020 WriteThread(const Options& options, const CrashedProcess::Thread& thread, 1021 int fatal_signal) { 1022 struct prstatus pr; 1023 memset(&pr, 0, sizeof(pr)); 1024 1025 pr.pr_info.si_signo = fatal_signal; 1026 pr.pr_cursig = fatal_signal; 1027 pr.pr_pid = thread.tid; 1028 #if defined(__mips__) 1029 memcpy(&pr.pr_reg, &thread.mcontext.gregs, sizeof(user_regs_struct)); 1030 #elif defined(__riscv) 1031 memcpy(&pr.pr_reg, &thread.mcontext.__gregs, sizeof(user_regs_struct)); 1032 #else 1033 memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); 1034 #endif 1035 1036 Nhdr nhdr; 1037 memset(&nhdr, 0, sizeof(nhdr)); 1038 nhdr.n_namesz = 5; 1039 nhdr.n_descsz = sizeof(struct prstatus); 1040 nhdr.n_type = NT_PRSTATUS; 1041 if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) || 1042 !writea(options.out_fd, "CORE\0\0\0\0", 8) || 1043 !writea(options.out_fd, &pr, sizeof(struct prstatus))) { 1044 return false; 1045 } 1046 1047 #if defined(__i386__) || defined(__x86_64__) 1048 nhdr.n_descsz = sizeof(user_fpregs_struct); 1049 nhdr.n_type = NT_FPREGSET; 1050 if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) || 1051 !writea(options.out_fd, "CORE\0\0\0\0", 8) || 1052 !writea(options.out_fd, &thread.fpregs, sizeof(user_fpregs_struct))) { 1053 return false; 1054 } 1055 #endif 1056 1057 #if defined(__i386__) 1058 nhdr.n_descsz = sizeof(user_fpxregs_struct); 1059 nhdr.n_type = NT_PRXFPREG; 1060 if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) || 1061 !writea(options.out_fd, "LINUX\0\0\0", 8) || 1062 !writea(options.out_fd, &thread.fpxregs, sizeof(user_fpxregs_struct))) { 1063 return false; 1064 } 1065 #endif 1066 1067 return true; 1068 } 1069 1070 static void 1071 ParseModuleStream(const Options& options, CrashedProcess* crashinfo, 1072 const MinidumpMemoryRange& range, 1073 const MinidumpMemoryRange& full_file) { 1074 if (options.verbose) { 1075 fputs("MD_MODULE_LIST_STREAM:\n", stderr); 1076 } 1077 const uint32_t num_mappings = *range.GetData<uint32_t>(0); 1078 for (unsigned i = 0; i < num_mappings; ++i) { 1079 CrashedProcess::Mapping mapping; 1080 const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>( 1081 range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i)); 1082 mapping.start_address = rawmodule->base_of_image; 1083 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; 1084 1085 if (crashinfo->mappings.find(mapping.start_address) == 1086 crashinfo->mappings.end()) { 1087 // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as 1088 // the former is a strict superset of the latter. 1089 crashinfo->mappings[mapping.start_address] = mapping; 1090 } 1091 1092 const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>( 1093 full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize)); 1094 char guid[40]; 1095 sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", 1096 record->signature.data1, record->signature.data2, 1097 record->signature.data3, 1098 record->signature.data4[0], record->signature.data4[1], 1099 record->signature.data4[2], record->signature.data4[3], 1100 record->signature.data4[4], record->signature.data4[5], 1101 record->signature.data4[6], record->signature.data4[7]); 1102 1103 string filename = full_file.GetAsciiMDString(rawmodule->module_name_rva); 1104 1105 CrashedProcess::Signature signature; 1106 strcpy(signature.guid, guid); 1107 signature.filename = filename; 1108 crashinfo->signatures[rawmodule->base_of_image] = signature; 1109 1110 if (options.verbose) { 1111 fprintf(stderr, "0x%" PRIx64 "-0x%" PRIx64 ", ChkSum: 0x%08X, GUID: %s, " 1112 " \"%s\"\n", 1113 rawmodule->base_of_image, 1114 rawmodule->base_of_image + rawmodule->size_of_image, 1115 rawmodule->checksum, guid, filename.c_str()); 1116 } 1117 } 1118 if (options.verbose) { 1119 fputs("\n\n", stderr); 1120 } 1121 } 1122 1123 static void 1124 AddDataToMapping(CrashedProcess* crashinfo, const string& data, 1125 uintptr_t addr) { 1126 for (std::map<uint64_t, CrashedProcess::Mapping>::iterator 1127 iter = crashinfo->mappings.begin(); 1128 iter != crashinfo->mappings.end(); 1129 ++iter) { 1130 if (addr >= iter->second.start_address && 1131 addr < iter->second.end_address) { 1132 CrashedProcess::Mapping mapping = iter->second; 1133 if ((addr & ~4095) != iter->second.start_address) { 1134 // If there are memory pages in the mapping prior to where the 1135 // data starts, truncate the existing mapping so that it ends with 1136 // the page immediately preceding the data region. 1137 iter->second.end_address = addr & ~4095; 1138 if (!mapping.filename.empty()) { 1139 // "mapping" is a copy of "iter->second". We are splitting the 1140 // existing mapping into two separate ones when we write the data 1141 // to the core file. The first one does not have any associated 1142 // data in the core file, the second one is backed by data that is 1143 // included with the core file. 1144 // If this mapping wasn't supposed to be anonymous, then we also 1145 // have to update the file offset upon splitting the mapping. 1146 mapping.offset += iter->second.end_address - 1147 iter->second.start_address; 1148 } 1149 } 1150 // Create a new mapping that contains the data contents. We often 1151 // limit the amount of data that is actually written to the core 1152 // file. But it is OK if the mapping itself extends past the end of 1153 // the data. 1154 mapping.start_address = addr & ~4095; 1155 mapping.data.assign(addr & 4095, 0).append(data); 1156 mapping.data.append(-mapping.data.size() & 4095, 0); 1157 crashinfo->mappings[mapping.start_address] = mapping; 1158 return; 1159 } 1160 } 1161 // Didn't find a suitable existing mapping for the data. Create a new one. 1162 CrashedProcess::Mapping mapping; 1163 mapping.permissions = PF_R | PF_W; 1164 mapping.start_address = addr & ~4095; 1165 mapping.end_address = 1166 (addr + data.size() + 4095) & ~4095; 1167 mapping.data.assign(addr & 4095, 0).append(data); 1168 mapping.data.append(-mapping.data.size() & 4095, 0); 1169 crashinfo->mappings[mapping.start_address] = mapping; 1170 } 1171 1172 static void 1173 AugmentMappings(const Options& options, CrashedProcess* crashinfo, 1174 const MinidumpMemoryRange& full_file) { 1175 // For each thread, find the memory mapping that matches the thread's stack. 1176 // Then adjust the mapping to include the stack dump. 1177 for (unsigned i = 0; i < crashinfo->threads.size(); ++i) { 1178 const CrashedProcess::Thread& thread = crashinfo->threads[i]; 1179 AddDataToMapping(crashinfo, 1180 string((char*)thread.stack, thread.stack_length), 1181 thread.stack_addr); 1182 } 1183 1184 // Create a new link map with information about DSOs. We move this map to 1185 // the beginning of the address space, as this area should always be 1186 // available. 1187 static const uintptr_t start_addr = 4096; 1188 string data; 1189 struct r_debug debug = { 0 }; 1190 debug.r_version = crashinfo->debug.version; 1191 debug.r_brk = (ElfW(Addr))crashinfo->debug.brk; 1192 debug.r_state = r_debug::RT_CONSISTENT; 1193 debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase; 1194 debug.r_map = crashinfo->debug.dso_count > 0 ? 1195 (struct link_map*)(start_addr + sizeof(debug)) : 0; 1196 data.append((char*)&debug, sizeof(debug)); 1197 1198 struct link_map* prev = 0; 1199 for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin(); 1200 iter != crashinfo->link_map.end(); 1201 ++iter) { 1202 struct link_map link_map = { 0 }; 1203 link_map.l_addr = (ElfW(Addr))iter->addr; 1204 link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map)); 1205 link_map.l_ld = (ElfW(Dyn)*)iter->ld; 1206 link_map.l_prev = prev; 1207 prev = (struct link_map*)(start_addr + data.size()); 1208 string filename = full_file.GetAsciiMDString(iter->name); 1209 1210 // Look up signature for this filename. If available, change filename 1211 // to point to GUID, instead. 1212 std::map<uintptr_t, CrashedProcess::Signature>::const_iterator sig = 1213 crashinfo->signatures.find((uintptr_t)iter->addr); 1214 if (sig != crashinfo->signatures.end()) { 1215 // At this point, we have: 1216 // old_filename: The path as found via SONAME (e.g. /lib/libpthread.so.0). 1217 // sig_filename: The path on disk (e.g. /lib/libpthread-2.19.so). 1218 const char* guid = sig->second.guid; 1219 string sig_filename = sig->second.filename; 1220 string old_filename = filename.empty() ? sig_filename : filename; 1221 string new_filename; 1222 1223 // First set up the leading path. We assume dirname always ends with a 1224 // trailing slash (as needed), so we won't be appending one manually. 1225 if (options.so_basedir.empty()) { 1226 string dirname; 1227 if (options.use_filename) { 1228 dirname = sig_filename; 1229 } else { 1230 dirname = old_filename; 1231 } 1232 size_t slash = dirname.find_last_of('/'); 1233 if (slash != string::npos) { 1234 new_filename = dirname.substr(0, slash + 1); 1235 } 1236 } else { 1237 new_filename = options.so_basedir; 1238 } 1239 1240 // Insert the module ID if requested. 1241 if (options.inc_guid && 1242 strcmp(guid, "00000000-0000-0000-0000-000000000000") != 0) { 1243 new_filename += guid; 1244 new_filename += "-"; 1245 } 1246 1247 // Decide whether we use the filename or the SONAME (where the SONAME tends 1248 // to be a symlink to the actual file). 1249 new_filename += google_breakpad::BaseName( 1250 options.use_filename ? sig_filename : old_filename); 1251 1252 if (filename != new_filename) { 1253 if (options.verbose) { 1254 fprintf(stderr, "0x%" PRIx64": rewriting mapping \"%s\" to \"%s\"\n", 1255 static_cast<uint64_t>(link_map.l_addr), 1256 filename.c_str(), new_filename.c_str()); 1257 } 1258 filename = new_filename; 1259 } 1260 } 1261 1262 if (std::distance(iter, crashinfo->link_map.end()) == 1) { 1263 link_map.l_next = 0; 1264 } else { 1265 link_map.l_next = (struct link_map*)(start_addr + data.size() + 1266 sizeof(link_map) + 1267 ((filename.size() + 8) & ~7)); 1268 } 1269 data.append((char*)&link_map, sizeof(link_map)); 1270 data.append(filename); 1271 data.append(8 - (filename.size() & 7), 0); 1272 } 1273 AddDataToMapping(crashinfo, data, start_addr); 1274 1275 // Map the page containing the _DYNAMIC array 1276 if (!crashinfo->dynamic_data.empty()) { 1277 // Make _DYNAMIC DT_DEBUG entry point to our link map 1278 for (int i = 0;; ++i) { 1279 ElfW(Dyn) dyn; 1280 if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) { 1281 no_dt_debug: 1282 if (options.verbose) { 1283 fprintf(stderr, "No DT_DEBUG entry found\n"); 1284 } 1285 return; 1286 } 1287 memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn), 1288 sizeof(dyn)); 1289 if (dyn.d_tag == DT_DEBUG) { 1290 crashinfo->dynamic_data.replace(i*sizeof(dyn) + 1291 offsetof(ElfW(Dyn), d_un.d_ptr), 1292 sizeof(start_addr), 1293 (char*)&start_addr, sizeof(start_addr)); 1294 break; 1295 } else if (dyn.d_tag == DT_NULL) { 1296 goto no_dt_debug; 1297 } 1298 } 1299 AddDataToMapping(crashinfo, crashinfo->dynamic_data, 1300 (uintptr_t)crashinfo->debug.dynamic); 1301 } 1302 } 1303 1304 int 1305 main(int argc, const char* argv[]) { 1306 Options options; 1307 SetupOptions(argc, argv, &options); 1308 1309 MemoryMappedFile mapped_file(options.minidump_path.c_str(), 0); 1310 if (!mapped_file.data()) { 1311 fprintf(stderr, "Failed to mmap dump file: %s: %s\n", 1312 options.minidump_path.c_str(), strerror(errno)); 1313 return 1; 1314 } 1315 1316 MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size()); 1317 1318 const MDRawHeader* header = dump.GetData<MDRawHeader>(0); 1319 1320 CrashedProcess crashinfo; 1321 1322 // Always check the system info first, as that allows us to tell whether 1323 // this is a minidump file that is compatible with our converter. 1324 bool ok = false; 1325 for (unsigned i = 0; i < header->stream_count; ++i) { 1326 const MDRawDirectory* dirent = 1327 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i); 1328 switch (dirent->stream_type) { 1329 case MD_SYSTEM_INFO_STREAM: 1330 ParseSystemInfo(options, &crashinfo, dump.Subrange(dirent->location), 1331 dump); 1332 ok = true; 1333 break; 1334 default: 1335 break; 1336 } 1337 } 1338 if (!ok) { 1339 fprintf(stderr, "Cannot determine input file format.\n"); 1340 exit(1); 1341 } 1342 1343 for (unsigned i = 0; i < header->stream_count; ++i) { 1344 const MDRawDirectory* dirent = 1345 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i); 1346 switch (dirent->stream_type) { 1347 case MD_THREAD_LIST_STREAM: 1348 ParseThreadList(options, &crashinfo, dump.Subrange(dirent->location), 1349 dump); 1350 break; 1351 case MD_LINUX_CPU_INFO: 1352 ParseCPUInfo(options, &crashinfo, dump.Subrange(dirent->location)); 1353 break; 1354 case MD_LINUX_PROC_STATUS: 1355 ParseProcessStatus(options, &crashinfo, 1356 dump.Subrange(dirent->location)); 1357 break; 1358 case MD_LINUX_LSB_RELEASE: 1359 ParseLSBRelease(options, &crashinfo, dump.Subrange(dirent->location)); 1360 break; 1361 case MD_LINUX_ENVIRON: 1362 ParseEnvironment(options, &crashinfo, dump.Subrange(dirent->location)); 1363 break; 1364 case MD_LINUX_MAPS: 1365 ParseMaps(options, &crashinfo, dump.Subrange(dirent->location)); 1366 break; 1367 case MD_LINUX_AUXV: 1368 ParseAuxVector(options, &crashinfo, dump.Subrange(dirent->location)); 1369 break; 1370 case MD_LINUX_CMD_LINE: 1371 ParseCmdLine(options, &crashinfo, dump.Subrange(dirent->location)); 1372 break; 1373 case MD_LINUX_DSO_DEBUG: 1374 ParseDSODebugInfo(options, &crashinfo, dump.Subrange(dirent->location), 1375 dump); 1376 break; 1377 case MD_EXCEPTION_STREAM: 1378 ParseExceptionStream(options, &crashinfo, 1379 dump.Subrange(dirent->location), dump); 1380 break; 1381 case MD_MODULE_LIST_STREAM: 1382 ParseModuleStream(options, &crashinfo, dump.Subrange(dirent->location), 1383 dump); 1384 break; 1385 default: 1386 if (options.verbose) 1387 fprintf(stderr, "Skipping %x\n", dirent->stream_type); 1388 } 1389 } 1390 1391 AugmentMappings(options, &crashinfo, dump); 1392 1393 // Write the ELF header. The file will look like: 1394 // ELF header 1395 // Phdr for the PT_NOTE 1396 // Phdr for each of the thread stacks 1397 // PT_NOTE 1398 // each of the thread stacks 1399 Ehdr ehdr; 1400 memset(&ehdr, 0, sizeof(Ehdr)); 1401 ehdr.e_ident[0] = ELFMAG0; 1402 ehdr.e_ident[1] = ELFMAG1; 1403 ehdr.e_ident[2] = ELFMAG2; 1404 ehdr.e_ident[3] = ELFMAG3; 1405 ehdr.e_ident[4] = ELF_CLASS; 1406 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; 1407 ehdr.e_ident[6] = EV_CURRENT; 1408 ehdr.e_type = ET_CORE; 1409 ehdr.e_machine = ELF_ARCH; 1410 ehdr.e_version = EV_CURRENT; 1411 ehdr.e_phoff = sizeof(Ehdr); 1412 ehdr.e_ehsize = sizeof(Ehdr); 1413 ehdr.e_phentsize= sizeof(Phdr); 1414 ehdr.e_phnum = 1 + // PT_NOTE 1415 crashinfo.mappings.size(); // memory mappings 1416 ehdr.e_shentsize= sizeof(Shdr); 1417 if (!writea(options.out_fd, &ehdr, sizeof(Ehdr))) 1418 return 1; 1419 1420 size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr); 1421 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + 1422 // sizeof(Nhdr) + 8 + sizeof(user) + 1423 sizeof(Nhdr) + 8 + crashinfo.auxv_length + 1424 crashinfo.threads.size() * ( 1425 (sizeof(Nhdr) + 8 + sizeof(prstatus)) 1426 #if defined(__i386__) || defined(__x86_64__) 1427 + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) 1428 #endif 1429 #if defined(__i386__) 1430 + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct) 1431 #endif 1432 ); 1433 1434 Phdr phdr; 1435 memset(&phdr, 0, sizeof(Phdr)); 1436 phdr.p_type = PT_NOTE; 1437 phdr.p_offset = offset; 1438 phdr.p_filesz = filesz; 1439 if (!writea(options.out_fd, &phdr, sizeof(phdr))) 1440 return 1; 1441 1442 phdr.p_type = PT_LOAD; 1443 phdr.p_align = 4096; 1444 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); 1445 if (note_align == phdr.p_align) 1446 note_align = 0; 1447 offset += note_align; 1448 1449 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter = 1450 crashinfo.mappings.begin(); 1451 iter != crashinfo.mappings.end(); ++iter) { 1452 const CrashedProcess::Mapping& mapping = iter->second; 1453 if (mapping.permissions == 0xFFFFFFFF) { 1454 // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to 1455 // MD_LINUX_MAPS). It lacks some of the information that we would like 1456 // to include. 1457 phdr.p_flags = PF_R; 1458 } else { 1459 phdr.p_flags = mapping.permissions; 1460 } 1461 phdr.p_vaddr = mapping.start_address; 1462 phdr.p_memsz = mapping.end_address - mapping.start_address; 1463 if (mapping.data.size()) { 1464 offset += filesz; 1465 filesz = mapping.data.size(); 1466 phdr.p_filesz = mapping.data.size(); 1467 phdr.p_offset = offset; 1468 } else { 1469 phdr.p_filesz = 0; 1470 phdr.p_offset = 0; 1471 } 1472 if (!writea(options.out_fd, &phdr, sizeof(phdr))) 1473 return 1; 1474 } 1475 1476 Nhdr nhdr; 1477 memset(&nhdr, 0, sizeof(nhdr)); 1478 nhdr.n_namesz = 5; 1479 nhdr.n_descsz = sizeof(prpsinfo); 1480 nhdr.n_type = NT_PRPSINFO; 1481 if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) || 1482 !writea(options.out_fd, "CORE\0\0\0\0", 8) || 1483 !writea(options.out_fd, &crashinfo.prps, sizeof(prpsinfo))) { 1484 return 1; 1485 } 1486 1487 nhdr.n_descsz = crashinfo.auxv_length; 1488 nhdr.n_type = NT_AUXV; 1489 if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) || 1490 !writea(options.out_fd, "CORE\0\0\0\0", 8) || 1491 !writea(options.out_fd, crashinfo.auxv, crashinfo.auxv_length)) { 1492 return 1; 1493 } 1494 1495 for (const auto& current_thread : crashinfo.threads) { 1496 if (current_thread.tid == crashinfo.exception.tid) { 1497 // Use the exception record's context for the crashed thread instead of 1498 // the thread's own context. For the crashed thread the thread's own 1499 // context is the state inside the exception handler. Using it would not 1500 // result in the expected stack trace from the time of the crash. 1501 // The stack memory has already been provided by current_thread. 1502 WriteThread(options, crashinfo.exception, crashinfo.fatal_signal); 1503 break; 1504 } 1505 } 1506 1507 for (const auto& current_thread : crashinfo.threads) { 1508 if (current_thread.tid != crashinfo.exception.tid) 1509 WriteThread(options, current_thread, 0); 1510 } 1511 1512 if (note_align) { 1513 google_breakpad::scoped_array<char> scratch(new char[note_align]); 1514 memset(scratch.get(), 0, note_align); 1515 if (!writea(options.out_fd, scratch.get(), note_align)) 1516 return 1; 1517 } 1518 1519 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter = 1520 crashinfo.mappings.begin(); 1521 iter != crashinfo.mappings.end(); ++iter) { 1522 const CrashedProcess::Mapping& mapping = iter->second; 1523 if (mapping.data.size()) { 1524 if (!writea(options.out_fd, mapping.data.c_str(), mapping.data.size())) 1525 return 1; 1526 } 1527 } 1528 1529 if (options.out_fd != STDOUT_FILENO) { 1530 close(options.out_fd); 1531 } 1532 1533 return 0; 1534 }