1 // Copyright (c) 2009, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
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/
34 // Usage: minidump-2-core [-v] 1234.dmp > core
49 #include "common/linux/memory_mapped_file.h"
50 #include "common/scoped_ptr.h"
51 #include "google_breakpad/common/minidump_format.h"
52 #include "third_party/lss/linux_syscall_support.h"
53 #include "tools/linux/md2core/minidump_memory_range.h"
56 #define ELF_CLASS ELFCLASS64
58 #define ELF_CLASS ELFCLASS32
60 #define Ehdr ElfW(Ehdr)
61 #define Phdr ElfW(Phdr)
62 #define Shdr ElfW(Shdr)
63 #define Nhdr ElfW(Nhdr)
64 #define auxv_t ElfW(auxv_t)
67 #if defined(__x86_64__)
68 #define ELF_ARCH EM_X86_64
69 #elif defined(__i386__)
70 #define ELF_ARCH EM_386
71 #elif defined(__arm__)
72 #define ELF_ARCH EM_ARM
73 #elif defined(__mips__)
74 #define ELF_ARCH EM_MIPS
78 // GLibc/ARM and Android/ARM both use 'user_regs' for the structure type
79 // containing core registers, while they use 'user_regs_struct' on other
80 // architectures. This file-local typedef simplifies the source code.
81 typedef user_regs user_regs_struct;
84 using google_breakpad::MemoryMappedFile;
85 using google_breakpad::MinidumpMemoryRange;
87 static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
89 static std::string g_custom_so_basedir;
91 static int usage(const char* argv0) {
92 fprintf(stderr, "Usage: %s [-v] <minidump file>\n", argv0);
96 // Write all of the given buffer, handling short writes and EINTR. Return true
99 writea(int fd, const void* idata, size_t length) {
100 const uint8_t* data = (const uint8_t*) idata;
103 while (done < length) {
106 r = write(fd, data + done, length - done);
107 } while (r == -1 && errno == EINTR);
117 /* Dynamically determines the byte sex of the system. Returns non-zero
118 * for big-endian machines.
120 static inline int sex() {
122 return !*(char *)&probe;
125 typedef struct elf_timeval { /* Time value with microsecond resolution */
126 long tv_sec; /* Seconds */
127 long tv_usec; /* Microseconds */
130 typedef struct elf_siginfo { /* Information about signal (unused) */
131 int32_t si_signo; /* Signal number */
132 int32_t si_code; /* Extra code */
133 int32_t si_errno; /* Errno */
136 typedef struct prstatus { /* Information about thread; includes CPU reg*/
137 elf_siginfo pr_info; /* Info associated with signal */
138 uint16_t pr_cursig; /* Current signal */
139 unsigned long pr_sigpend; /* Set of pending signals */
140 unsigned long pr_sighold; /* Set of held signals */
141 pid_t pr_pid; /* Process ID */
142 pid_t pr_ppid; /* Parent's process ID */
143 pid_t pr_pgrp; /* Group ID */
144 pid_t pr_sid; /* Session ID */
145 elf_timeval pr_utime; /* User time */
146 elf_timeval pr_stime; /* System time */
147 elf_timeval pr_cutime; /* Cumulative user time */
148 elf_timeval pr_cstime; /* Cumulative system time */
149 user_regs_struct pr_reg; /* CPU registers */
150 uint32_t pr_fpvalid; /* True if math co-processor being used */
153 typedef struct prpsinfo { /* Information about process */
154 unsigned char pr_state; /* Numeric process state */
155 char pr_sname; /* Char for pr_state */
156 unsigned char pr_zomb; /* Zombie */
157 signed char pr_nice; /* Nice val */
158 unsigned long pr_flag; /* Flags */
159 #if defined(__x86_64__) || defined(__mips__)
160 uint32_t pr_uid; /* User ID */
161 uint32_t pr_gid; /* Group ID */
163 uint16_t pr_uid; /* User ID */
164 uint16_t pr_gid; /* Group ID */
166 pid_t pr_pid; /* Process ID */
167 pid_t pr_ppid; /* Parent's process ID */
168 pid_t pr_pgrp; /* Group ID */
169 pid_t pr_sid; /* Session ID */
170 char pr_fname[16]; /* Filename of executable */
171 char pr_psargs[80]; /* Initial part of arg list */
174 // We parse the minidump file and keep the parsed information in this structure
175 struct CrashedProcess {
180 memset(&prps, 0, sizeof(prps));
182 memset(&debug, 0, sizeof(debug));
187 : permissions(0xFFFFFFFF),
193 uint32_t permissions;
194 uint64_t start_address, end_address, offset;
195 std::string filename;
198 std::map<uint64_t, Mapping> mappings;
205 user_regs_struct regs;
206 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
207 user_fpregs_struct fpregs;
209 #if defined(__i386__)
210 user_fpxregs_struct fpxregs;
212 uintptr_t stack_addr;
213 const uint8_t* stack;
216 std::vector<Thread> threads;
223 std::map<uintptr_t, std::string> signatures;
225 std::string dynamic_data;
227 std::vector<MDRawLinkMap> link_map;
230 #if defined(__i386__)
232 U32(const uint8_t* data) {
234 memcpy(&v, data, sizeof(v));
239 U16(const uint8_t* data) {
241 memcpy(&v, data, sizeof(v));
246 ParseThreadRegisters(CrashedProcess::Thread* thread,
247 const MinidumpMemoryRange& range) {
248 const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0);
250 thread->regs.ebx = rawregs->ebx;
251 thread->regs.ecx = rawregs->ecx;
252 thread->regs.edx = rawregs->edx;
253 thread->regs.esi = rawregs->esi;
254 thread->regs.edi = rawregs->edi;
255 thread->regs.ebp = rawregs->ebp;
256 thread->regs.eax = rawregs->eax;
257 thread->regs.xds = rawregs->ds;
258 thread->regs.xes = rawregs->es;
259 thread->regs.xfs = rawregs->fs;
260 thread->regs.xgs = rawregs->gs;
261 thread->regs.orig_eax = rawregs->eax;
262 thread->regs.eip = rawregs->eip;
263 thread->regs.xcs = rawregs->cs;
264 thread->regs.eflags = rawregs->eflags;
265 thread->regs.esp = rawregs->esp;
266 thread->regs.xss = rawregs->ss;
268 thread->fpregs.cwd = rawregs->float_save.control_word;
269 thread->fpregs.swd = rawregs->float_save.status_word;
270 thread->fpregs.twd = rawregs->float_save.tag_word;
271 thread->fpregs.fip = rawregs->float_save.error_offset;
272 thread->fpregs.fcs = rawregs->float_save.error_selector;
273 thread->fpregs.foo = rawregs->float_save.data_offset;
274 thread->fpregs.fos = rawregs->float_save.data_selector;
275 memcpy(thread->fpregs.st_space, rawregs->float_save.register_area,
278 thread->fpxregs.cwd = rawregs->float_save.control_word;
279 thread->fpxregs.swd = rawregs->float_save.status_word;
280 thread->fpxregs.twd = rawregs->float_save.tag_word;
281 thread->fpxregs.fop = U16(rawregs->extended_registers + 6);
282 thread->fpxregs.fip = U16(rawregs->extended_registers + 8);
283 thread->fpxregs.fcs = U16(rawregs->extended_registers + 12);
284 thread->fpxregs.foo = U16(rawregs->extended_registers + 16);
285 thread->fpxregs.fos = U16(rawregs->extended_registers + 20);
286 thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24);
287 memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128);
288 memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128);
290 #elif defined(__x86_64__)
292 ParseThreadRegisters(CrashedProcess::Thread* thread,
293 const MinidumpMemoryRange& range) {
294 const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0);
296 thread->regs.r15 = rawregs->r15;
297 thread->regs.r14 = rawregs->r14;
298 thread->regs.r13 = rawregs->r13;
299 thread->regs.r12 = rawregs->r12;
300 thread->regs.rbp = rawregs->rbp;
301 thread->regs.rbx = rawregs->rbx;
302 thread->regs.r11 = rawregs->r11;
303 thread->regs.r10 = rawregs->r10;
304 thread->regs.r9 = rawregs->r9;
305 thread->regs.r8 = rawregs->r8;
306 thread->regs.rax = rawregs->rax;
307 thread->regs.rcx = rawregs->rcx;
308 thread->regs.rdx = rawregs->rdx;
309 thread->regs.rsi = rawregs->rsi;
310 thread->regs.rdi = rawregs->rdi;
311 thread->regs.orig_rax = rawregs->rax;
312 thread->regs.rip = rawregs->rip;
313 thread->regs.cs = rawregs->cs;
314 thread->regs.eflags = rawregs->eflags;
315 thread->regs.rsp = rawregs->rsp;
316 thread->regs.ss = rawregs->ss;
317 thread->regs.fs_base = 0;
318 thread->regs.gs_base = 0;
319 thread->regs.ds = rawregs->ds;
320 thread->regs.es = rawregs->es;
321 thread->regs.fs = rawregs->fs;
322 thread->regs.gs = rawregs->gs;
324 thread->fpregs.cwd = rawregs->flt_save.control_word;
325 thread->fpregs.swd = rawregs->flt_save.status_word;
326 thread->fpregs.ftw = rawregs->flt_save.tag_word;
327 thread->fpregs.fop = rawregs->flt_save.error_opcode;
328 thread->fpregs.rip = rawregs->flt_save.error_offset;
329 thread->fpregs.rdp = rawregs->flt_save.data_offset;
330 thread->fpregs.mxcsr = rawregs->flt_save.mx_csr;
331 thread->fpregs.mxcr_mask = rawregs->flt_save.mx_csr_mask;
332 memcpy(thread->fpregs.st_space, rawregs->flt_save.float_registers, 8 * 16);
333 memcpy(thread->fpregs.xmm_space, rawregs->flt_save.xmm_registers, 16 * 16);
335 #elif defined(__arm__)
337 ParseThreadRegisters(CrashedProcess::Thread* thread,
338 const MinidumpMemoryRange& range) {
339 const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0);
341 thread->regs.uregs[0] = rawregs->iregs[0];
342 thread->regs.uregs[1] = rawregs->iregs[1];
343 thread->regs.uregs[2] = rawregs->iregs[2];
344 thread->regs.uregs[3] = rawregs->iregs[3];
345 thread->regs.uregs[4] = rawregs->iregs[4];
346 thread->regs.uregs[5] = rawregs->iregs[5];
347 thread->regs.uregs[6] = rawregs->iregs[6];
348 thread->regs.uregs[7] = rawregs->iregs[7];
349 thread->regs.uregs[8] = rawregs->iregs[8];
350 thread->regs.uregs[9] = rawregs->iregs[9];
351 thread->regs.uregs[10] = rawregs->iregs[10];
352 thread->regs.uregs[11] = rawregs->iregs[11];
353 thread->regs.uregs[12] = rawregs->iregs[12];
354 thread->regs.uregs[13] = rawregs->iregs[13];
355 thread->regs.uregs[14] = rawregs->iregs[14];
356 thread->regs.uregs[15] = rawregs->iregs[15];
358 thread->regs.uregs[16] = rawregs->cpsr;
359 thread->regs.uregs[17] = 0; // what is ORIG_r0 exactly?
361 #elif defined(__mips__)
363 ParseThreadRegisters(CrashedProcess::Thread* thread,
364 const MinidumpMemoryRange& range) {
365 const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0);
367 for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
368 thread->regs.regs[i] = rawregs->iregs[i];
370 thread->regs.lo = rawregs->mdlo;
371 thread->regs.hi = rawregs->mdhi;
372 thread->regs.epc = rawregs->epc;
373 thread->regs.badvaddr = rawregs->badvaddr;
374 thread->regs.status = rawregs->status;
375 thread->regs.cause = rawregs->cause;
377 for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
378 thread->fpregs.regs[i] = rawregs->float_save.regs[i];
380 thread->fpregs.fpcsr = rawregs->float_save.fpcsr;
381 thread->fpregs.fir = rawregs->float_save.fir;
384 #error "This code has not been ported to your platform yet"
388 ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
389 const MinidumpMemoryRange& full_file) {
390 const uint32_t num_threads = *range.GetData<uint32_t>(0);
393 "MD_THREAD_LIST_STREAM:\n"
398 for (unsigned i = 0; i < num_threads; ++i) {
399 CrashedProcess::Thread thread;
400 memset(&thread, 0, sizeof(thread));
401 const MDRawThread* rawthread =
402 range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i);
403 thread.tid = rawthread->thread_id;
404 thread.stack_addr = rawthread->stack.start_of_memory_range;
405 MinidumpMemoryRange stack_range =
406 full_file.Subrange(rawthread->stack.memory);
407 thread.stack = stack_range.data();
408 thread.stack_length = rawthread->stack.memory.data_size;
410 ParseThreadRegisters(&thread,
411 full_file.Subrange(rawthread->thread_context));
413 crashinfo->threads.push_back(thread);
418 ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
419 const MinidumpMemoryRange& full_file) {
420 const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
422 fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
425 #if defined(__i386__)
426 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
428 "This version of minidump-2-core only supports x86 (32bit)%s.\n",
429 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ?
430 ",\nbut the minidump file is from a 64bit machine" : "");
433 #elif defined(__x86_64__)
434 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
436 "This version of minidump-2-core only supports x86 (64bit)%s.\n",
437 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ?
438 ",\nbut the minidump file is from a 32bit machine" : "");
441 #elif defined(__arm__)
442 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
444 "This version of minidump-2-core only supports ARM (32bit).\n");
447 #elif defined(__mips__)
448 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
450 "This version of minidump-2-core only supports mips (32bit).\n");
454 #error "This code has not been ported to your platform yet"
456 if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
458 sysinfo->platform_id != MD_OS_NACL) {
459 fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n");
465 "MD_SYSTEM_INFO_STREAM:\n"
467 "Number of processors: %d\n"
468 "Processor level: %d\n"
469 "Processor model: %d\n"
470 "Processor stepping: %d\n",
471 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86
473 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
475 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
477 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
480 sysinfo->number_of_processors,
481 sysinfo->processor_level,
482 sysinfo->processor_revision >> 8,
483 sysinfo->processor_revision & 0xFF);
484 if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
485 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) {
486 fputs("Vendor id: ", stderr);
488 (const char *)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0,
489 sizeof(sysinfo->cpu.x86_cpu_info.vendor_id));
490 fwrite(sysinfo->cpu.x86_cpu_info.vendor_id,
491 nul ? nul - (const char *)&sysinfo->cpu.x86_cpu_info.vendor_id[0]
492 : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr);
495 fprintf(stderr, "OS: %s\n",
496 full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
497 fputs("\n\n", stderr);
502 ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
504 fputs("MD_LINUX_CPU_INFO:\n", stderr);
505 fwrite(range.data(), range.length(), 1, stderr);
506 fputs("\n\n\n", stderr);
511 ParseProcessStatus(CrashedProcess* crashinfo,
512 const MinidumpMemoryRange& range) {
514 fputs("MD_LINUX_PROC_STATUS:\n", stderr);
515 fwrite(range.data(), range.length(), 1, stderr);
516 fputs("\n\n", stderr);
521 ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
523 fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
524 fwrite(range.data(), range.length(), 1, stderr);
525 fputs("\n\n", stderr);
530 ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
532 fputs("MD_LINUX_MAPS:\n", stderr);
533 fwrite(range.data(), range.length(), 1, stderr);
535 for (const uint8_t* ptr = range.data();
536 ptr < range.data() + range.length();) {
537 const uint8_t* eol = (uint8_t*)memchr(ptr, '\n',
538 range.data() + range.length() - ptr);
539 std::string line((const char*)ptr,
540 eol ? eol - ptr : range.data() + range.length() - ptr);
541 ptr = eol ? eol + 1 : range.data() + range.length();
542 unsigned long long start, stop, offset;
543 char* permissions = NULL;
544 char* filename = NULL;
545 sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
546 &start, &stop, &permissions, &offset, &filename);
547 if (filename && *filename == '/') {
548 CrashedProcess::Mapping mapping;
549 mapping.permissions = 0;
550 if (strchr(permissions, 'r')) {
551 mapping.permissions |= PF_R;
553 if (strchr(permissions, 'w')) {
554 mapping.permissions |= PF_W;
556 if (strchr(permissions, 'x')) {
557 mapping.permissions |= PF_X;
559 mapping.start_address = start;
560 mapping.end_address = stop;
561 mapping.offset = offset;
563 mapping.filename = filename;
565 crashinfo->mappings[mapping.start_address] = mapping;
571 fputs("\n\n\n", stderr);
576 ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
578 fputs("MD_LINUX_ENVIRON:\n", stderr);
579 char* env = new char[range.length()];
580 memcpy(env, range.data(), range.length());
582 for (char *ptr = env;;) {
583 ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env));
587 if (ptr > env && ptr[-1] == '\n') {
588 if (++nul_count > 5) {
589 // Some versions of Chrome try to rewrite the process' command line
590 // in a way that causes the environment to be corrupted. Afterwards,
591 // part of the environment will contain the trailing bit of the
592 // command line. The rest of the environment will be filled with
594 // We detect this corruption by counting the number of consecutive
595 // NUL bytes. Normally, we would not expect any consecutive NUL
596 // bytes. But we are conservative and only suppress printing of
597 // the environment if we see at least five consecutive NULs.
598 fputs("Environment has been corrupted; no data available", stderr);
606 fwrite(env, range.length(), 1, stderr);
609 fputs("\n\n\n", stderr);
614 ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
615 // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
616 // when dumping /proc/$x/maps
617 if (range.length() > 17) {
618 // The AUXV vector contains binary data, whereas the maps always begin
619 // with an 8+ digit hex address followed by a hyphen and another 8+ digit
622 memcpy(addresses, range.data(), 17);
623 addresses[17] = '\000';
624 if (strspn(addresses, "0123456789abcdef-") == 17) {
625 ParseMaps(crashinfo, range);
630 crashinfo->auxv = range.data();
631 crashinfo->auxv_length = range.length();
635 ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
636 // The command line is supposed to use NUL bytes to separate arguments.
637 // As Chrome rewrites its own command line and (incorrectly) substitutes
638 // spaces, this is often not the case in our minidump files.
639 const char* cmdline = (const char*) range.data();
641 fputs("MD_LINUX_CMD_LINE:\n", stderr);
643 for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { }
644 fputs("argv[0] = \"", stderr);
645 fwrite(cmdline, i, 1, stderr);
646 fputs("\"\n", stderr);
647 for (unsigned j = ++i, argc = 1; j < range.length(); ++j) {
648 if (!cmdline[j] || cmdline[j] == ' ') {
649 fprintf(stderr, "argv[%d] = \"", argc++);
650 fwrite(cmdline + i, j - i, 1, stderr);
651 fputs("\"\n", stderr);
655 fputs("\n\n", stderr);
658 const char *binary_name = cmdline;
659 for (size_t i = 0; i < range.length(); ++i) {
660 if (cmdline[i] == '/') {
661 binary_name = cmdline + i + 1;
662 } else if (cmdline[i] == 0 || cmdline[i] == ' ') {
663 static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1;
664 static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1;
665 memset(crashinfo->prps.pr_fname, 0, fname_len + 1);
666 memset(crashinfo->prps.pr_psargs, 0, args_len + 1);
667 unsigned len = cmdline + i - binary_name;
668 memcpy(crashinfo->prps.pr_fname, binary_name,
669 len > fname_len ? fname_len : len);
671 len = range.length() > args_len ? args_len : range.length();
672 memcpy(crashinfo->prps.pr_psargs, cmdline, len);
673 for (unsigned j = 0; j < len; ++j) {
674 if (crashinfo->prps.pr_psargs[j] == 0)
675 crashinfo->prps.pr_psargs[j] = ' ';
683 ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
684 const MinidumpMemoryRange& full_file) {
685 const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
691 "MD_LINUX_DSO_DEBUG:\n"
693 "Number of DSOs: %d\n"
695 "Dynamic loader at: %p\n"
703 crashinfo->debug = *debug;
704 if (range.length() > sizeof(MDRawDebug)) {
705 char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug);
706 crashinfo->dynamic_data.assign(dynamic_data,
707 range.length() - sizeof(MDRawDebug));
709 if (debug->map != kInvalidMDRVA) {
710 for (unsigned int i = 0; i < debug->dso_count; ++i) {
711 const MDRawLinkMap* link_map =
712 full_file.GetArrayElement<MDRawLinkMap>(debug->map, i);
716 "#%03d: %p, %p, \"%s\"\n",
717 i, link_map->addr, link_map->ld,
718 full_file.GetAsciiMDString(link_map->name).c_str());
720 crashinfo->link_map.push_back(*link_map);
725 fputs("\n\n", stderr);
730 ParseExceptionStream(CrashedProcess* crashinfo,
731 const MinidumpMemoryRange& range) {
732 const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0);
733 crashinfo->crashing_tid = exp->thread_id;
734 crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
738 WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
740 memset(&pr, 0, sizeof(pr));
742 pr.pr_info.si_signo = fatal_signal;
743 pr.pr_cursig = fatal_signal;
744 pr.pr_pid = thread.tid;
745 memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
748 memset(&nhdr, 0, sizeof(nhdr));
750 nhdr.n_descsz = sizeof(struct prstatus);
751 nhdr.n_type = NT_PRSTATUS;
752 if (!writea(1, &nhdr, sizeof(nhdr)) ||
753 !writea(1, "CORE\0\0\0\0", 8) ||
754 !writea(1, &pr, sizeof(struct prstatus))) {
758 #if defined(__i386__) || defined(__x86_64__)
759 nhdr.n_descsz = sizeof(user_fpregs_struct);
760 nhdr.n_type = NT_FPREGSET;
761 if (!writea(1, &nhdr, sizeof(nhdr)) ||
762 !writea(1, "CORE\0\0\0\0", 8) ||
763 !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) {
768 #if defined(__i386__)
769 nhdr.n_descsz = sizeof(user_fpxregs_struct);
770 nhdr.n_type = NT_PRXFPREG;
771 if (!writea(1, &nhdr, sizeof(nhdr)) ||
772 !writea(1, "LINUX\0\0\0", 8) ||
773 !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
782 ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
783 const MinidumpMemoryRange& full_file) {
785 fputs("MD_MODULE_LIST_STREAM:\n", stderr);
787 const uint32_t num_mappings = *range.GetData<uint32_t>(0);
788 for (unsigned i = 0; i < num_mappings; ++i) {
789 CrashedProcess::Mapping mapping;
790 const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>(
791 range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i));
792 mapping.start_address = rawmodule->base_of_image;
793 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
795 if (crashinfo->mappings.find(mapping.start_address) ==
796 crashinfo->mappings.end()) {
797 // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as
798 // the former is a strict superset of the latter.
799 crashinfo->mappings[mapping.start_address] = mapping;
802 const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
803 full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
805 sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
806 record->signature.data1, record->signature.data2,
807 record->signature.data3,
808 record->signature.data4[0], record->signature.data4[1],
809 record->signature.data4[2], record->signature.data4[3],
810 record->signature.data4[4], record->signature.data4[5],
811 record->signature.data4[6], record->signature.data4[7]);
812 std::string filename =
813 full_file.GetAsciiMDString(rawmodule->module_name_rva);
814 size_t slash = filename.find_last_of('/');
815 std::string basename = slash == std::string::npos ?
816 filename : filename.substr(slash + 1);
817 if (strcmp(guid, "00000000-0000-0000-0000-000000000000")) {
819 if (!g_custom_so_basedir.empty())
820 prefix = g_custom_so_basedir;
822 prefix = std::string("/var/lib/breakpad/") + guid + "-" + basename;
824 crashinfo->signatures[rawmodule->base_of_image] = prefix + basename;
828 fprintf(stderr, "0x%08llX-0x%08llX, ChkSum: 0x%08X, GUID: %s, \"%s\"\n",
829 (unsigned long long)rawmodule->base_of_image,
830 (unsigned long long)rawmodule->base_of_image +
831 rawmodule->size_of_image,
832 rawmodule->checksum, guid, filename.c_str());
836 fputs("\n\n", stderr);
841 AddDataToMapping(CrashedProcess* crashinfo, const std::string& data,
843 for (std::map<uint64_t, CrashedProcess::Mapping>::iterator
844 iter = crashinfo->mappings.begin();
845 iter != crashinfo->mappings.end();
847 if (addr >= iter->second.start_address &&
848 addr < iter->second.end_address) {
849 CrashedProcess::Mapping mapping = iter->second;
850 if ((addr & ~4095) != iter->second.start_address) {
851 // If there are memory pages in the mapping prior to where the
852 // data starts, truncate the existing mapping so that it ends with
853 // the page immediately preceding the data region.
854 iter->second.end_address = addr & ~4095;
855 if (!mapping.filename.empty()) {
856 // "mapping" is a copy of "iter->second". We are splitting the
857 // existing mapping into two separate ones when we write the data
858 // to the core file. The first one does not have any associated
859 // data in the core file, the second one is backed by data that is
860 // included with the core file.
861 // If this mapping wasn't supposed to be anonymous, then we also
862 // have to update the file offset upon splitting the mapping.
863 mapping.offset += iter->second.end_address -
864 iter->second.start_address;
867 // Create a new mapping that contains the data contents. We often
868 // limit the amount of data that is actually written to the core
869 // file. But it is OK if the mapping itself extends past the end of
871 mapping.start_address = addr & ~4095;
872 mapping.data.assign(addr & 4095, 0).append(data);
873 mapping.data.append(-mapping.data.size() & 4095, 0);
874 crashinfo->mappings[mapping.start_address] = mapping;
878 // Didn't find a suitable existing mapping for the data. Create a new one.
879 CrashedProcess::Mapping mapping;
880 mapping.permissions = PF_R | PF_W;
881 mapping.start_address = addr & ~4095;
882 mapping.end_address =
883 (addr + data.size() + 4095) & ~4095;
884 mapping.data.assign(addr & 4095, 0).append(data);
885 mapping.data.append(-mapping.data.size() & 4095, 0);
886 crashinfo->mappings[mapping.start_address] = mapping;
890 AugmentMappings(CrashedProcess* crashinfo,
891 const MinidumpMemoryRange& full_file) {
892 // For each thread, find the memory mapping that matches the thread's stack.
893 // Then adjust the mapping to include the stack dump.
894 for (unsigned i = 0; i < crashinfo->threads.size(); ++i) {
895 const CrashedProcess::Thread& thread = crashinfo->threads[i];
896 AddDataToMapping(crashinfo,
897 std::string((char *)thread.stack, thread.stack_length),
901 // Create a new link map with information about DSOs. We move this map to
902 // the beginning of the address space, as this area should always be
904 static const uintptr_t start_addr = 4096;
906 struct r_debug debug = { 0 };
907 debug.r_version = crashinfo->debug.version;
908 debug.r_brk = (ElfW(Addr))crashinfo->debug.brk;
909 debug.r_state = r_debug::RT_CONSISTENT;
910 debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase;
911 debug.r_map = crashinfo->debug.dso_count > 0 ?
912 (struct link_map*)(start_addr + sizeof(debug)) : 0;
913 data.append((char*)&debug, sizeof(debug));
915 struct link_map* prev = 0;
916 for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin();
917 iter != crashinfo->link_map.end();
919 struct link_map link_map = { 0 };
920 link_map.l_addr = (ElfW(Addr))iter->addr;
921 link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map));
922 link_map.l_ld = (ElfW(Dyn)*)iter->ld;
923 link_map.l_prev = prev;
924 prev = (struct link_map*)(start_addr + data.size());
925 std::string filename = full_file.GetAsciiMDString(iter->name);
927 // Look up signature for this filename. If available, change filename
928 // to point to GUID, instead.
929 std::map<uintptr_t, std::string>::const_iterator guid =
930 crashinfo->signatures.find((uintptr_t)iter->addr);
931 if (guid != crashinfo->signatures.end()) {
932 filename = guid->second;
935 if (std::distance(iter, crashinfo->link_map.end()) == 1) {
938 link_map.l_next = (struct link_map*)(start_addr + data.size() +
940 ((filename.size() + 8) & ~7));
942 data.append((char*)&link_map, sizeof(link_map));
943 data.append(filename);
944 data.append(8 - (filename.size() & 7), 0);
946 AddDataToMapping(crashinfo, data, start_addr);
948 // Map the page containing the _DYNAMIC array
949 if (!crashinfo->dynamic_data.empty()) {
950 // Make _DYNAMIC DT_DEBUG entry point to our link map
951 for (int i = 0;; ++i) {
953 if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) {
956 fprintf(stderr, "No DT_DEBUG entry found\n");
960 memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn),
962 if (dyn.d_tag == DT_DEBUG) {
963 crashinfo->dynamic_data.replace(i*sizeof(dyn) +
964 offsetof(ElfW(Dyn), d_un.d_ptr),
966 (char*)&start_addr, sizeof(start_addr));
968 } else if (dyn.d_tag == DT_NULL) {
972 AddDataToMapping(crashinfo, crashinfo->dynamic_data,
973 (uintptr_t)crashinfo->debug.dynamic);
978 main(int argc, char** argv) {
980 while (argi < argc && argv[argi][0] == '-') {
981 if (!strcmp(argv[argi], "-v")) {
983 } else if (!strcmp(argv[argi], "--sobasedir")) {
986 fprintf(stderr, "--sobasedir expects an argument.");
987 return usage(argv[0]);
990 g_custom_so_basedir = argv[argi];
992 return usage(argv[0]);
997 if (argc != argi + 1)
998 return usage(argv[0]);
1000 MemoryMappedFile mapped_file(argv[argi], 0);
1001 if (!mapped_file.data()) {
1002 fprintf(stderr, "Failed to mmap dump file\n");
1006 MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
1008 const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
1010 CrashedProcess crashinfo;
1012 // Always check the system info first, as that allows us to tell whether
1013 // this is a minidump file that is compatible with our converter.
1015 for (unsigned i = 0; i < header->stream_count; ++i) {
1016 const MDRawDirectory* dirent =
1017 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1018 switch (dirent->stream_type) {
1019 case MD_SYSTEM_INFO_STREAM:
1020 ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1028 fprintf(stderr, "Cannot determine input file format.\n");
1032 for (unsigned i = 0; i < header->stream_count; ++i) {
1033 const MDRawDirectory* dirent =
1034 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1035 switch (dirent->stream_type) {
1036 case MD_THREAD_LIST_STREAM:
1037 ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
1039 case MD_LINUX_CPU_INFO:
1040 ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location));
1042 case MD_LINUX_PROC_STATUS:
1043 ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location));
1045 case MD_LINUX_LSB_RELEASE:
1046 ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location));
1048 case MD_LINUX_ENVIRON:
1049 ParseEnvironment(&crashinfo, dump.Subrange(dirent->location));
1052 ParseMaps(&crashinfo, dump.Subrange(dirent->location));
1055 ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
1057 case MD_LINUX_CMD_LINE:
1058 ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
1060 case MD_LINUX_DSO_DEBUG:
1061 ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1063 case MD_EXCEPTION_STREAM:
1064 ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
1066 case MD_MODULE_LIST_STREAM:
1067 ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump);
1071 fprintf(stderr, "Skipping %x\n", dirent->stream_type);
1075 AugmentMappings(&crashinfo, dump);
1077 // Write the ELF header. The file will look like:
1079 // Phdr for the PT_NOTE
1080 // Phdr for each of the thread stacks
1082 // each of the thread stacks
1084 memset(&ehdr, 0, sizeof(Ehdr));
1085 ehdr.e_ident[0] = ELFMAG0;
1086 ehdr.e_ident[1] = ELFMAG1;
1087 ehdr.e_ident[2] = ELFMAG2;
1088 ehdr.e_ident[3] = ELFMAG3;
1089 ehdr.e_ident[4] = ELF_CLASS;
1090 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
1091 ehdr.e_ident[6] = EV_CURRENT;
1092 ehdr.e_type = ET_CORE;
1093 ehdr.e_machine = ELF_ARCH;
1094 ehdr.e_version = EV_CURRENT;
1095 ehdr.e_phoff = sizeof(Ehdr);
1096 ehdr.e_ehsize = sizeof(Ehdr);
1097 ehdr.e_phentsize= sizeof(Phdr);
1098 ehdr.e_phnum = 1 + // PT_NOTE
1099 crashinfo.mappings.size(); // memory mappings
1100 ehdr.e_shentsize= sizeof(Shdr);
1101 if (!writea(1, &ehdr, sizeof(Ehdr)))
1104 size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr);
1105 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
1106 // sizeof(Nhdr) + 8 + sizeof(user) +
1107 sizeof(Nhdr) + 8 + crashinfo.auxv_length +
1108 crashinfo.threads.size() * (
1109 (sizeof(Nhdr) + 8 + sizeof(prstatus))
1110 #if defined(__i386__) || defined(__x86_64__)
1111 + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct)
1113 #if defined(__i386__)
1114 + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
1119 memset(&phdr, 0, sizeof(Phdr));
1120 phdr.p_type = PT_NOTE;
1121 phdr.p_offset = offset;
1122 phdr.p_filesz = filesz;
1123 if (!writea(1, &phdr, sizeof(phdr)))
1126 phdr.p_type = PT_LOAD;
1127 phdr.p_align = 4096;
1128 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
1129 if (note_align == phdr.p_align)
1131 offset += note_align;
1133 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1134 crashinfo.mappings.begin();
1135 iter != crashinfo.mappings.end(); ++iter) {
1136 const CrashedProcess::Mapping& mapping = iter->second;
1137 if (mapping.permissions == 0xFFFFFFFF) {
1138 // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to
1139 // MD_LINUX_MAPS). It lacks some of the information that we would like
1141 phdr.p_flags = PF_R;
1143 phdr.p_flags = mapping.permissions;
1145 phdr.p_vaddr = mapping.start_address;
1146 phdr.p_memsz = mapping.end_address - mapping.start_address;
1147 if (mapping.data.size()) {
1149 filesz = mapping.data.size();
1150 phdr.p_filesz = mapping.data.size();
1151 phdr.p_offset = offset;
1156 if (!writea(1, &phdr, sizeof(phdr)))
1161 memset(&nhdr, 0, sizeof(nhdr));
1163 nhdr.n_descsz = sizeof(prpsinfo);
1164 nhdr.n_type = NT_PRPSINFO;
1165 if (!writea(1, &nhdr, sizeof(nhdr)) ||
1166 !writea(1, "CORE\0\0\0\0", 8) ||
1167 !writea(1, &crashinfo.prps, sizeof(prpsinfo))) {
1171 nhdr.n_descsz = crashinfo.auxv_length;
1172 nhdr.n_type = NT_AUXV;
1173 if (!writea(1, &nhdr, sizeof(nhdr)) ||
1174 !writea(1, "CORE\0\0\0\0", 8) ||
1175 !writea(1, crashinfo.auxv, crashinfo.auxv_length)) {
1179 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1180 if (crashinfo.threads[i].tid == crashinfo.crashing_tid) {
1181 WriteThread(crashinfo.threads[i], crashinfo.fatal_signal);
1186 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1187 if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
1188 WriteThread(crashinfo.threads[i], 0);
1192 google_breakpad::scoped_array<char> scratch(new char[note_align]);
1193 memset(scratch.get(), 0, note_align);
1194 if (!writea(1, scratch.get(), note_align))
1198 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1199 crashinfo.mappings.begin();
1200 iter != crashinfo.mappings.end(); ++iter) {
1201 const CrashedProcess::Mapping& mapping = iter->second;
1202 if (mapping.data.size()) {
1203 if (!writea(1, mapping.data.c_str(), mapping.data.size()))