summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc1428
1 files changed, 1428 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc b/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc
new file mode 100644
index 0000000000..a60be32354
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc
@@ -0,0 +1,1428 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Converts a minidump file to a core file which gdb can read.
+// Large parts lifted from the userspace core dumper:
+// http://code.google.com/p/google-coredumper/
+
+#include <elf.h>
+#include <errno.h>
+#include <limits.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/user.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "common/linux/memory_mapped_file.h"
+#include "common/minidump_type_helper.h"
+#include "common/path_helper.h"
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "third_party/lss/linux_syscall_support.h"
+#include "tools/linux/md2core/minidump_memory_range.h"
+
+#if ULONG_MAX == 0xffffffffffffffff
+ #define ELF_CLASS ELFCLASS64
+#else
+ #define ELF_CLASS ELFCLASS32
+#endif
+#define Ehdr ElfW(Ehdr)
+#define Phdr ElfW(Phdr)
+#define Shdr ElfW(Shdr)
+#define Nhdr ElfW(Nhdr)
+#define auxv_t ElfW(auxv_t)
+
+
+#if defined(__x86_64__)
+ #define ELF_ARCH EM_X86_64
+#elif defined(__i386__)
+ #define ELF_ARCH EM_386
+#elif defined(__arm__)
+ #define ELF_ARCH EM_ARM
+#elif defined(__mips__)
+ #define ELF_ARCH EM_MIPS
+#elif defined(__aarch64__)
+ #define ELF_ARCH EM_AARCH64
+#endif
+
+#if defined(__arm__)
+// GLibc/ARM and Android/ARM both use 'user_regs' for the structure type
+// containing core registers, while they use 'user_regs_struct' on other
+// architectures. This file-local typedef simplifies the source code.
+typedef user_regs user_regs_struct;
+#elif defined (__mips__)
+// This file-local typedef simplifies the source code.
+typedef gregset_t user_regs_struct;
+#endif
+
+using google_breakpad::MDTypeHelper;
+using google_breakpad::MemoryMappedFile;
+using google_breakpad::MinidumpMemoryRange;
+
+typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawDebug MDRawDebug;
+typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawLinkMap MDRawLinkMap;
+
+static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
+
+struct Options {
+ string minidump_path;
+ bool verbose;
+ int out_fd;
+ bool use_filename;
+ bool inc_guid;
+ string so_basedir;
+};
+
+static void
+Usage(int argc, const char* argv[]) {
+ fprintf(stderr,
+ "Usage: %s [options] <minidump file>\n"
+ "\n"
+ "Convert a minidump file into a core file (often for use by gdb).\n"
+ "\n"
+ "The shared library list will by default have filenames as the runtime expects.\n"
+ "There are many flags to control the output names though to make it easier to\n"
+ "integrate with your debug environment (e.g. gdb).\n"
+ " Default: /lib64/libpthread.so.0\n"
+ " -f: /lib64/libpthread-2.19.so\n"
+ " -i: /lib64/<module id>-libpthread.so.0\n"
+ " -f -i: /lib64/<module id>-libpthread-2.19.so\n"
+ " -S /foo/: /foo/libpthread.so.0\n"
+ "\n"
+ "Options:\n"
+ " -v Enable verbose output\n"
+ " -o <file> Write coredump to specified file (otherwise use stdout).\n"
+ " -f Use the filename rather than the soname in the sharedlib list.\n"
+ " The soname is what the runtime system uses, but the filename is\n"
+ " how it's stored on disk.\n"
+ " -i Prefix sharedlib names with ID (when available). This makes it\n"
+ " easier to have a single directory full of symbols.\n"
+ " -S <dir> Set soname base directory. This will force all debug/symbol\n"
+ " lookups to be done in this directory rather than the filesystem\n"
+ " layout as it exists in the crashing image. This path should end\n"
+ " with a slash if it's a directory. e.g. /var/lib/breakpad/\n"
+ "", google_breakpad::BaseName(argv[0]).c_str());
+}
+
+static void
+SetupOptions(int argc, const char* argv[], Options* options) {
+ extern int optind;
+ int ch;
+ const char* output_file = NULL;
+
+ // Initialize the options struct as needed.
+ options->verbose = false;
+ options->use_filename = false;
+ options->inc_guid = false;
+
+ while ((ch = getopt(argc, (char * const *)argv, "fhio:S:v")) != -1) {
+ switch (ch) {
+ case 'h':
+ Usage(argc, argv);
+ exit(0);
+ break;
+ case '?':
+ Usage(argc, argv);
+ exit(1);
+ break;
+
+ case 'f':
+ options->use_filename = true;
+ break;
+ case 'i':
+ options->inc_guid = true;
+ break;
+ case 'o':
+ output_file = optarg;
+ break;
+ case 'S':
+ options->so_basedir = optarg;
+ break;
+ case 'v':
+ options->verbose = true;
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1) {
+ fprintf(stderr, "%s: Missing minidump file\n", argv[0]);
+ Usage(argc, argv);
+ exit(1);
+ }
+
+ if (output_file == NULL || !strcmp(output_file, "-")) {
+ options->out_fd = STDOUT_FILENO;
+ } else {
+ options->out_fd = open(output_file, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (options->out_fd == -1) {
+ fprintf(stderr, "%s: could not open output %s: %s\n", argv[0],
+ output_file, strerror(errno));
+ exit(1);
+ }
+ }
+
+ options->minidump_path = argv[optind];
+}
+
+// Write all of the given buffer, handling short writes and EINTR. Return true
+// iff successful.
+static bool
+writea(int fd, const void* idata, size_t length) {
+ const uint8_t* data = (const uint8_t*) idata;
+
+ size_t done = 0;
+ while (done < length) {
+ ssize_t r;
+ do {
+ r = write(fd, data + done, length - done);
+ } while (r == -1 && errno == EINTR);
+
+ if (r < 1)
+ return false;
+ done += r;
+ }
+
+ return true;
+}
+
+/* Dynamically determines the byte sex of the system. Returns non-zero
+ * for big-endian machines.
+ */
+static inline int sex() {
+ int probe = 1;
+ return !*(char *)&probe;
+}
+
+typedef struct elf_timeval { /* Time value with microsecond resolution */
+ long tv_sec; /* Seconds */
+ long tv_usec; /* Microseconds */
+} elf_timeval;
+
+typedef struct _elf_siginfo { /* Information about signal (unused) */
+ int32_t si_signo; /* Signal number */
+ int32_t si_code; /* Extra code */
+ int32_t si_errno; /* Errno */
+} _elf_siginfo;
+
+typedef struct prstatus { /* Information about thread; includes CPU reg*/
+ _elf_siginfo pr_info; /* Info associated with signal */
+ uint16_t pr_cursig; /* Current signal */
+ unsigned long pr_sigpend; /* Set of pending signals */
+ unsigned long pr_sighold; /* Set of held signals */
+ pid_t pr_pid; /* Process ID */
+ pid_t pr_ppid; /* Parent's process ID */
+ pid_t pr_pgrp; /* Group ID */
+ pid_t pr_sid; /* Session ID */
+ elf_timeval pr_utime; /* User time */
+ elf_timeval pr_stime; /* System time */
+ elf_timeval pr_cutime; /* Cumulative user time */
+ elf_timeval pr_cstime; /* Cumulative system time */
+ user_regs_struct pr_reg; /* CPU registers */
+ uint32_t pr_fpvalid; /* True if math co-processor being used */
+} prstatus;
+
+typedef struct prpsinfo { /* Information about process */
+ unsigned char pr_state; /* Numeric process state */
+ char pr_sname; /* Char for pr_state */
+ unsigned char pr_zomb; /* Zombie */
+ signed char pr_nice; /* Nice val */
+ unsigned long pr_flag; /* Flags */
+#if defined(__x86_64__) || defined(__mips__)
+ uint32_t pr_uid; /* User ID */
+ uint32_t pr_gid; /* Group ID */
+#else
+ uint16_t pr_uid; /* User ID */
+ uint16_t pr_gid; /* Group ID */
+#endif
+ pid_t pr_pid; /* Process ID */
+ pid_t pr_ppid; /* Parent's process ID */
+ pid_t pr_pgrp; /* Group ID */
+ pid_t pr_sid; /* Session ID */
+ char pr_fname[16]; /* Filename of executable */
+ char pr_psargs[80]; /* Initial part of arg list */
+} prpsinfo;
+
+// We parse the minidump file and keep the parsed information in this structure
+struct CrashedProcess {
+ CrashedProcess()
+ : crashing_tid(-1),
+ auxv(NULL),
+ auxv_length(0) {
+ memset(&prps, 0, sizeof(prps));
+ prps.pr_sname = 'R';
+ memset(&debug, 0, sizeof(debug));
+ }
+
+ struct Mapping {
+ Mapping()
+ : permissions(0xFFFFFFFF),
+ start_address(0),
+ end_address(0),
+ offset(0) {
+ }
+
+ uint32_t permissions;
+ uint64_t start_address, end_address, offset;
+ // The name we write out to the core.
+ string filename;
+ string data;
+ };
+ std::map<uint64_t, Mapping> mappings;
+
+ pid_t crashing_tid;
+ int fatal_signal;
+
+ struct Thread {
+ pid_t tid;
+#if defined(__mips__)
+ mcontext_t mcontext;
+#else
+ user_regs_struct regs;
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ user_fpregs_struct fpregs;
+#endif
+#if defined(__i386__)
+ user_fpxregs_struct fpxregs;
+#endif
+#if defined(__aarch64__)
+ user_fpsimd_struct fpregs;
+#endif
+ uintptr_t stack_addr;
+ const uint8_t* stack;
+ size_t stack_length;
+ };
+ std::vector<Thread> threads;
+
+ const uint8_t* auxv;
+ size_t auxv_length;
+
+ prpsinfo prps;
+
+ // The GUID/filename from MD_MODULE_LIST_STREAM entries.
+ // We gather them for merging later on into the list of maps.
+ struct Signature {
+ char guid[40];
+ string filename;
+ };
+ std::map<uintptr_t, Signature> signatures;
+
+ string dynamic_data;
+ MDRawDebug debug;
+ std::vector<MDRawLinkMap> link_map;
+};
+
+#if defined(__i386__)
+static uint32_t
+U32(const uint8_t* data) {
+ uint32_t v;
+ memcpy(&v, data, sizeof(v));
+ return v;
+}
+
+static uint16_t
+U16(const uint8_t* data) {
+ uint16_t v;
+ memcpy(&v, data, sizeof(v));
+ return v;
+}
+
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+ const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0);
+
+ thread->regs.ebx = rawregs->ebx;
+ thread->regs.ecx = rawregs->ecx;
+ thread->regs.edx = rawregs->edx;
+ thread->regs.esi = rawregs->esi;
+ thread->regs.edi = rawregs->edi;
+ thread->regs.ebp = rawregs->ebp;
+ thread->regs.eax = rawregs->eax;
+ thread->regs.xds = rawregs->ds;
+ thread->regs.xes = rawregs->es;
+ thread->regs.xfs = rawregs->fs;
+ thread->regs.xgs = rawregs->gs;
+ thread->regs.orig_eax = rawregs->eax;
+ thread->regs.eip = rawregs->eip;
+ thread->regs.xcs = rawregs->cs;
+ thread->regs.eflags = rawregs->eflags;
+ thread->regs.esp = rawregs->esp;
+ thread->regs.xss = rawregs->ss;
+
+ thread->fpregs.cwd = rawregs->float_save.control_word;
+ thread->fpregs.swd = rawregs->float_save.status_word;
+ thread->fpregs.twd = rawregs->float_save.tag_word;
+ thread->fpregs.fip = rawregs->float_save.error_offset;
+ thread->fpregs.fcs = rawregs->float_save.error_selector;
+ thread->fpregs.foo = rawregs->float_save.data_offset;
+ thread->fpregs.fos = rawregs->float_save.data_selector;
+ memcpy(thread->fpregs.st_space, rawregs->float_save.register_area,
+ 10 * 8);
+
+ thread->fpxregs.cwd = rawregs->float_save.control_word;
+ thread->fpxregs.swd = rawregs->float_save.status_word;
+ thread->fpxregs.twd = rawregs->float_save.tag_word;
+ thread->fpxregs.fop = U16(rawregs->extended_registers + 6);
+ thread->fpxregs.fip = U16(rawregs->extended_registers + 8);
+ thread->fpxregs.fcs = U16(rawregs->extended_registers + 12);
+ thread->fpxregs.foo = U16(rawregs->extended_registers + 16);
+ thread->fpxregs.fos = U16(rawregs->extended_registers + 20);
+ thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24);
+ memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128);
+ memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128);
+}
+#elif defined(__x86_64__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+ const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0);
+
+ thread->regs.r15 = rawregs->r15;
+ thread->regs.r14 = rawregs->r14;
+ thread->regs.r13 = rawregs->r13;
+ thread->regs.r12 = rawregs->r12;
+ thread->regs.rbp = rawregs->rbp;
+ thread->regs.rbx = rawregs->rbx;
+ thread->regs.r11 = rawregs->r11;
+ thread->regs.r10 = rawregs->r10;
+ thread->regs.r9 = rawregs->r9;
+ thread->regs.r8 = rawregs->r8;
+ thread->regs.rax = rawregs->rax;
+ thread->regs.rcx = rawregs->rcx;
+ thread->regs.rdx = rawregs->rdx;
+ thread->regs.rsi = rawregs->rsi;
+ thread->regs.rdi = rawregs->rdi;
+ thread->regs.orig_rax = rawregs->rax;
+ thread->regs.rip = rawregs->rip;
+ thread->regs.cs = rawregs->cs;
+ thread->regs.eflags = rawregs->eflags;
+ thread->regs.rsp = rawregs->rsp;
+ thread->regs.ss = rawregs->ss;
+ thread->regs.fs_base = 0;
+ thread->regs.gs_base = 0;
+ thread->regs.ds = rawregs->ds;
+ thread->regs.es = rawregs->es;
+ thread->regs.fs = rawregs->fs;
+ thread->regs.gs = rawregs->gs;
+
+ thread->fpregs.cwd = rawregs->flt_save.control_word;
+ thread->fpregs.swd = rawregs->flt_save.status_word;
+ thread->fpregs.ftw = rawregs->flt_save.tag_word;
+ thread->fpregs.fop = rawregs->flt_save.error_opcode;
+ thread->fpregs.rip = rawregs->flt_save.error_offset;
+ thread->fpregs.rdp = rawregs->flt_save.data_offset;
+ thread->fpregs.mxcsr = rawregs->flt_save.mx_csr;
+ thread->fpregs.mxcr_mask = rawregs->flt_save.mx_csr_mask;
+ memcpy(thread->fpregs.st_space, rawregs->flt_save.float_registers, 8 * 16);
+ memcpy(thread->fpregs.xmm_space, rawregs->flt_save.xmm_registers, 16 * 16);
+}
+#elif defined(__arm__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+ const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0);
+
+ thread->regs.uregs[0] = rawregs->iregs[0];
+ thread->regs.uregs[1] = rawregs->iregs[1];
+ thread->regs.uregs[2] = rawregs->iregs[2];
+ thread->regs.uregs[3] = rawregs->iregs[3];
+ thread->regs.uregs[4] = rawregs->iregs[4];
+ thread->regs.uregs[5] = rawregs->iregs[5];
+ thread->regs.uregs[6] = rawregs->iregs[6];
+ thread->regs.uregs[7] = rawregs->iregs[7];
+ thread->regs.uregs[8] = rawregs->iregs[8];
+ thread->regs.uregs[9] = rawregs->iregs[9];
+ thread->regs.uregs[10] = rawregs->iregs[10];
+ thread->regs.uregs[11] = rawregs->iregs[11];
+ thread->regs.uregs[12] = rawregs->iregs[12];
+ thread->regs.uregs[13] = rawregs->iregs[13];
+ thread->regs.uregs[14] = rawregs->iregs[14];
+ thread->regs.uregs[15] = rawregs->iregs[15];
+
+ thread->regs.uregs[16] = rawregs->cpsr;
+ thread->regs.uregs[17] = 0; // what is ORIG_r0 exactly?
+}
+#elif defined(__aarch64__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+#define COPY_REGS(rawregs) \
+ do { \
+ for (int i = 0; i < 31; ++i) \
+ thread->regs.regs[i] = rawregs->iregs[i]; \
+ thread->regs.sp = rawregs->iregs[MD_CONTEXT_ARM64_REG_SP]; \
+ thread->regs.pc = rawregs->iregs[MD_CONTEXT_ARM64_REG_PC]; \
+ thread->regs.pstate = rawregs->cpsr; \
+ \
+ memcpy(thread->fpregs.vregs, rawregs->float_save.regs, 8 * 32); \
+ thread->fpregs.fpsr = rawregs->float_save.fpsr; \
+ thread->fpregs.fpcr = rawregs->float_save.fpcr; \
+ } while (false)
+
+ if (range.length() == sizeof(MDRawContextARM64_Old)) {
+ const MDRawContextARM64_Old* rawregs =
+ range.GetData<MDRawContextARM64_Old>(0);
+ COPY_REGS(rawregs);
+ } else {
+ const MDRawContextARM64* rawregs = range.GetData<MDRawContextARM64>(0);
+ COPY_REGS(rawregs);
+ }
+#undef COPY_REGS
+}
+#elif defined(__mips__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+ const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0);
+
+ for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
+ thread->mcontext.gregs[i] = rawregs->iregs[i];
+
+ thread->mcontext.pc = rawregs->epc;
+
+ thread->mcontext.mdlo = rawregs->mdlo;
+ thread->mcontext.mdhi = rawregs->mdhi;
+
+ thread->mcontext.hi1 = rawregs->hi[0];
+ thread->mcontext.lo1 = rawregs->lo[0];
+ thread->mcontext.hi2 = rawregs->hi[1];
+ thread->mcontext.lo2 = rawregs->lo[1];
+ thread->mcontext.hi3 = rawregs->hi[2];
+ thread->mcontext.lo3 = rawregs->lo[2];
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) {
+ thread->mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs =
+ rawregs->float_save.regs[i];
+ }
+
+ thread->mcontext.fpc_csr = rawregs->float_save.fpcsr;
+#if _MIPS_SIM == _ABIO32
+ thread->mcontext.fpc_eir = rawregs->float_save.fir;
+#endif
+}
+#else
+#error "This code has not been ported to your platform yet"
+#endif
+
+static void
+ParseThreadList(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range,
+ const MinidumpMemoryRange& full_file) {
+ const uint32_t num_threads = *range.GetData<uint32_t>(0);
+ if (options.verbose) {
+ fprintf(stderr,
+ "MD_THREAD_LIST_STREAM:\n"
+ "Found %d threads\n"
+ "\n\n",
+ num_threads);
+ }
+ for (unsigned i = 0; i < num_threads; ++i) {
+ CrashedProcess::Thread thread;
+ memset(&thread, 0, sizeof(thread));
+ const MDRawThread* rawthread =
+ range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i);
+ thread.tid = rawthread->thread_id;
+ thread.stack_addr = rawthread->stack.start_of_memory_range;
+ MinidumpMemoryRange stack_range =
+ full_file.Subrange(rawthread->stack.memory);
+ thread.stack = stack_range.data();
+ thread.stack_length = rawthread->stack.memory.data_size;
+
+ ParseThreadRegisters(&thread,
+ full_file.Subrange(rawthread->thread_context));
+
+ crashinfo->threads.push_back(thread);
+ }
+}
+
+static void
+ParseSystemInfo(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range,
+ const MinidumpMemoryRange& full_file) {
+ const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
+ if (!sysinfo) {
+ fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
+ exit(1);
+ }
+#if defined(__i386__)
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports x86 (32bit)%s.\n",
+ sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ?
+ ",\nbut the minidump file is from a 64bit machine" : "");
+ exit(1);
+ }
+#elif defined(__x86_64__)
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports x86 (64bit)%s.\n",
+ sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ?
+ ",\nbut the minidump file is from a 32bit machine" : "");
+ exit(1);
+ }
+#elif defined(__arm__)
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports ARM (32bit).\n");
+ exit(1);
+ }
+#elif defined(__aarch64__)
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM64_OLD) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports ARM (64bit).\n");
+ exit(1);
+ }
+#elif defined(__mips__)
+# if _MIPS_SIM == _ABIO32
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports mips o32 (32bit).\n");
+ exit(1);
+ }
+# elif _MIPS_SIM == _ABI64
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS64) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports mips n64 (64bit).\n");
+ exit(1);
+ }
+# else
+# error "This mips ABI is currently not supported (n32)"
+# endif
+#else
+#error "This code has not been ported to your platform yet"
+#endif
+ if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
+ "Linux") &&
+ sysinfo->platform_id != MD_OS_NACL) {
+ fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n");
+ exit(1);
+ }
+
+ if (options.verbose) {
+ fprintf(stderr,
+ "MD_SYSTEM_INFO_STREAM:\n"
+ "Architecture: %s\n"
+ "Number of processors: %d\n"
+ "Processor level: %d\n"
+ "Processor model: %d\n"
+ "Processor stepping: %d\n",
+ sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86
+ ? "i386"
+ : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
+ ? "x86-64"
+ : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
+ ? "ARM"
+ : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
+ ? "MIPS"
+ : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS64
+ ? "MIPS64"
+ : "???",
+ sysinfo->number_of_processors,
+ sysinfo->processor_level,
+ sysinfo->processor_revision >> 8,
+ sysinfo->processor_revision & 0xFF);
+ if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
+ sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) {
+ fputs("Vendor id: ", stderr);
+ const char *nul =
+ (const char *)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0,
+ sizeof(sysinfo->cpu.x86_cpu_info.vendor_id));
+ fwrite(sysinfo->cpu.x86_cpu_info.vendor_id,
+ nul ? nul - (const char *)&sysinfo->cpu.x86_cpu_info.vendor_id[0]
+ : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr);
+ fputs("\n", stderr);
+ }
+ fprintf(stderr, "OS: %s\n",
+ full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
+ fputs("\n\n", stderr);
+ }
+}
+
+static void
+ParseCPUInfo(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ if (options.verbose) {
+ fputs("MD_LINUX_CPU_INFO:\n", stderr);
+ fwrite(range.data(), range.length(), 1, stderr);
+ fputs("\n\n\n", stderr);
+ }
+}
+
+static void
+ParseProcessStatus(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ if (options.verbose) {
+ fputs("MD_LINUX_PROC_STATUS:\n", stderr);
+ fwrite(range.data(), range.length(), 1, stderr);
+ fputs("\n\n", stderr);
+ }
+}
+
+static void
+ParseLSBRelease(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ if (options.verbose) {
+ fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
+ fwrite(range.data(), range.length(), 1, stderr);
+ fputs("\n\n", stderr);
+ }
+}
+
+static void
+ParseMaps(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ if (options.verbose) {
+ fputs("MD_LINUX_MAPS:\n", stderr);
+ fwrite(range.data(), range.length(), 1, stderr);
+ }
+ for (const uint8_t* ptr = range.data();
+ ptr < range.data() + range.length();) {
+ const uint8_t* eol = (uint8_t*)memchr(ptr, '\n',
+ range.data() + range.length() - ptr);
+ string line((const char*)ptr,
+ eol ? eol - ptr : range.data() + range.length() - ptr);
+ ptr = eol ? eol + 1 : range.data() + range.length();
+ unsigned long long start, stop, offset;
+ char* permissions = NULL;
+ char* filename = NULL;
+ sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
+ &start, &stop, &permissions, &offset, &filename);
+ if (filename && *filename == '/') {
+ CrashedProcess::Mapping mapping;
+ mapping.permissions = 0;
+ if (strchr(permissions, 'r')) {
+ mapping.permissions |= PF_R;
+ }
+ if (strchr(permissions, 'w')) {
+ mapping.permissions |= PF_W;
+ }
+ if (strchr(permissions, 'x')) {
+ mapping.permissions |= PF_X;
+ }
+ mapping.start_address = start;
+ mapping.end_address = stop;
+ mapping.offset = offset;
+ if (filename) {
+ mapping.filename = filename;
+ }
+ crashinfo->mappings[mapping.start_address] = mapping;
+ }
+ free(permissions);
+ free(filename);
+ }
+ if (options.verbose) {
+ fputs("\n\n\n", stderr);
+ }
+}
+
+static void
+ParseEnvironment(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ if (options.verbose) {
+ fputs("MD_LINUX_ENVIRON:\n", stderr);
+ char* env = new char[range.length()];
+ memcpy(env, range.data(), range.length());
+ int nul_count = 0;
+ for (char *ptr = env;;) {
+ ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env));
+ if (!ptr) {
+ break;
+ }
+ if (ptr > env && ptr[-1] == '\n') {
+ if (++nul_count > 5) {
+ // Some versions of Chrome try to rewrite the process' command line
+ // in a way that causes the environment to be corrupted. Afterwards,
+ // part of the environment will contain the trailing bit of the
+ // command line. The rest of the environment will be filled with
+ // NUL bytes.
+ // We detect this corruption by counting the number of consecutive
+ // NUL bytes. Normally, we would not expect any consecutive NUL
+ // bytes. But we are conservative and only suppress printing of
+ // the environment if we see at least five consecutive NULs.
+ fputs("Environment has been corrupted; no data available", stderr);
+ goto env_corrupted;
+ }
+ } else {
+ nul_count = 0;
+ }
+ *ptr = '\n';
+ }
+ fwrite(env, range.length(), 1, stderr);
+ env_corrupted:
+ delete[] env;
+ fputs("\n\n\n", stderr);
+ }
+}
+
+static void
+ParseAuxVector(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
+ // when dumping /proc/$x/maps
+ if (range.length() > 17) {
+ // The AUXV vector contains binary data, whereas the maps always begin
+ // with an 8+ digit hex address followed by a hyphen and another 8+ digit
+ // address.
+ char addresses[18];
+ memcpy(addresses, range.data(), 17);
+ addresses[17] = '\000';
+ if (strspn(addresses, "0123456789abcdef-") == 17) {
+ ParseMaps(options, crashinfo, range);
+ return;
+ }
+ }
+
+ crashinfo->auxv = range.data();
+ crashinfo->auxv_length = range.length();
+}
+
+static void
+ParseCmdLine(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ // The command line is supposed to use NUL bytes to separate arguments.
+ // As Chrome rewrites its own command line and (incorrectly) substitutes
+ // spaces, this is often not the case in our minidump files.
+ const char* cmdline = (const char*) range.data();
+ if (options.verbose) {
+ fputs("MD_LINUX_CMD_LINE:\n", stderr);
+ unsigned i = 0;
+ for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { }
+ fputs("argv[0] = \"", stderr);
+ fwrite(cmdline, i, 1, stderr);
+ fputs("\"\n", stderr);
+ for (unsigned j = ++i, argc = 1; j < range.length(); ++j) {
+ if (!cmdline[j] || cmdline[j] == ' ') {
+ fprintf(stderr, "argv[%d] = \"", argc++);
+ fwrite(cmdline + i, j - i, 1, stderr);
+ fputs("\"\n", stderr);
+ i = j + 1;
+ }
+ }
+ fputs("\n\n", stderr);
+ }
+
+ const char *binary_name = cmdline;
+ for (size_t i = 0; i < range.length(); ++i) {
+ if (cmdline[i] == '/') {
+ binary_name = cmdline + i + 1;
+ } else if (cmdline[i] == 0 || cmdline[i] == ' ') {
+ static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1;
+ static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1;
+ memset(crashinfo->prps.pr_fname, 0, fname_len + 1);
+ memset(crashinfo->prps.pr_psargs, 0, args_len + 1);
+ unsigned len = cmdline + i - binary_name;
+ memcpy(crashinfo->prps.pr_fname, binary_name,
+ len > fname_len ? fname_len : len);
+
+ len = range.length() > args_len ? args_len : range.length();
+ memcpy(crashinfo->prps.pr_psargs, cmdline, len);
+ for (unsigned j = 0; j < len; ++j) {
+ if (crashinfo->prps.pr_psargs[j] == 0)
+ crashinfo->prps.pr_psargs[j] = ' ';
+ }
+ break;
+ }
+ }
+}
+
+static void
+ParseDSODebugInfo(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range,
+ const MinidumpMemoryRange& full_file) {
+ const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
+ if (!debug) {
+ return;
+ }
+ if (options.verbose) {
+ fprintf(stderr,
+ "MD_LINUX_DSO_DEBUG:\n"
+ "Version: %d\n"
+ "Number of DSOs: %d\n"
+ "Brk handler: 0x%" PRIx64 "\n"
+ "Dynamic loader at: 0x%" PRIx64 "\n"
+ "_DYNAMIC: 0x%" PRIx64 "\n",
+ debug->version,
+ debug->dso_count,
+ static_cast<uint64_t>(debug->brk),
+ static_cast<uint64_t>(debug->ldbase),
+ static_cast<uint64_t>(debug->dynamic));
+ }
+ crashinfo->debug = *debug;
+ if (range.length() > sizeof(MDRawDebug)) {
+ char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug);
+ crashinfo->dynamic_data.assign(dynamic_data,
+ range.length() - sizeof(MDRawDebug));
+ }
+ if (debug->map != kInvalidMDRVA) {
+ for (unsigned int i = 0; i < debug->dso_count; ++i) {
+ const MDRawLinkMap* link_map =
+ full_file.GetArrayElement<MDRawLinkMap>(debug->map, i);
+ if (link_map) {
+ if (options.verbose) {
+ fprintf(stderr,
+ "#%03d: %" PRIx64 ", %" PRIx64 ", \"%s\"\n",
+ i, static_cast<uint64_t>(link_map->addr),
+ static_cast<uint64_t>(link_map->ld),
+ full_file.GetAsciiMDString(link_map->name).c_str());
+ }
+ crashinfo->link_map.push_back(*link_map);
+ }
+ }
+ }
+ if (options.verbose) {
+ fputs("\n\n", stderr);
+ }
+}
+
+static void
+ParseExceptionStream(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range) {
+ const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0);
+ crashinfo->crashing_tid = exp->thread_id;
+ crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
+}
+
+static bool
+WriteThread(const Options& options, const CrashedProcess::Thread& thread,
+ int fatal_signal) {
+ struct prstatus pr;
+ memset(&pr, 0, sizeof(pr));
+
+ pr.pr_info.si_signo = fatal_signal;
+ pr.pr_cursig = fatal_signal;
+ pr.pr_pid = thread.tid;
+#if defined(__mips__)
+ memcpy(&pr.pr_reg, &thread.mcontext.gregs, sizeof(user_regs_struct));
+#else
+ memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
+#endif
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(struct prstatus);
+ nhdr.n_type = NT_PRSTATUS;
+ if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) ||
+ !writea(options.out_fd, "CORE\0\0\0\0", 8) ||
+ !writea(options.out_fd, &pr, sizeof(struct prstatus))) {
+ return false;
+ }
+
+#if defined(__i386__) || defined(__x86_64__)
+ nhdr.n_descsz = sizeof(user_fpregs_struct);
+ nhdr.n_type = NT_FPREGSET;
+ if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) ||
+ !writea(options.out_fd, "CORE\0\0\0\0", 8) ||
+ !writea(options.out_fd, &thread.fpregs, sizeof(user_fpregs_struct))) {
+ return false;
+ }
+#endif
+
+#if defined(__i386__)
+ nhdr.n_descsz = sizeof(user_fpxregs_struct);
+ nhdr.n_type = NT_PRXFPREG;
+ if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) ||
+ !writea(options.out_fd, "LINUX\0\0\0", 8) ||
+ !writea(options.out_fd, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+static void
+ParseModuleStream(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& range,
+ const MinidumpMemoryRange& full_file) {
+ if (options.verbose) {
+ fputs("MD_MODULE_LIST_STREAM:\n", stderr);
+ }
+ const uint32_t num_mappings = *range.GetData<uint32_t>(0);
+ for (unsigned i = 0; i < num_mappings; ++i) {
+ CrashedProcess::Mapping mapping;
+ const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>(
+ range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i));
+ mapping.start_address = rawmodule->base_of_image;
+ mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
+
+ if (crashinfo->mappings.find(mapping.start_address) ==
+ crashinfo->mappings.end()) {
+ // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as
+ // the former is a strict superset of the latter.
+ crashinfo->mappings[mapping.start_address] = mapping;
+ }
+
+ const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
+ full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
+ char guid[40];
+ sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ record->signature.data1, record->signature.data2,
+ record->signature.data3,
+ record->signature.data4[0], record->signature.data4[1],
+ record->signature.data4[2], record->signature.data4[3],
+ record->signature.data4[4], record->signature.data4[5],
+ record->signature.data4[6], record->signature.data4[7]);
+
+ string filename = full_file.GetAsciiMDString(rawmodule->module_name_rva);
+
+ CrashedProcess::Signature signature;
+ strcpy(signature.guid, guid);
+ signature.filename = filename;
+ crashinfo->signatures[rawmodule->base_of_image] = signature;
+
+ if (options.verbose) {
+ fprintf(stderr, "0x%" PRIx64 "-0x%" PRIx64 ", ChkSum: 0x%08X, GUID: %s, "
+ " \"%s\"\n",
+ rawmodule->base_of_image,
+ rawmodule->base_of_image + rawmodule->size_of_image,
+ rawmodule->checksum, guid, filename.c_str());
+ }
+ }
+ if (options.verbose) {
+ fputs("\n\n", stderr);
+ }
+}
+
+static void
+AddDataToMapping(CrashedProcess* crashinfo, const string& data,
+ uintptr_t addr) {
+ for (std::map<uint64_t, CrashedProcess::Mapping>::iterator
+ iter = crashinfo->mappings.begin();
+ iter != crashinfo->mappings.end();
+ ++iter) {
+ if (addr >= iter->second.start_address &&
+ addr < iter->second.end_address) {
+ CrashedProcess::Mapping mapping = iter->second;
+ if ((addr & ~4095) != iter->second.start_address) {
+ // If there are memory pages in the mapping prior to where the
+ // data starts, truncate the existing mapping so that it ends with
+ // the page immediately preceding the data region.
+ iter->second.end_address = addr & ~4095;
+ if (!mapping.filename.empty()) {
+ // "mapping" is a copy of "iter->second". We are splitting the
+ // existing mapping into two separate ones when we write the data
+ // to the core file. The first one does not have any associated
+ // data in the core file, the second one is backed by data that is
+ // included with the core file.
+ // If this mapping wasn't supposed to be anonymous, then we also
+ // have to update the file offset upon splitting the mapping.
+ mapping.offset += iter->second.end_address -
+ iter->second.start_address;
+ }
+ }
+ // Create a new mapping that contains the data contents. We often
+ // limit the amount of data that is actually written to the core
+ // file. But it is OK if the mapping itself extends past the end of
+ // the data.
+ mapping.start_address = addr & ~4095;
+ mapping.data.assign(addr & 4095, 0).append(data);
+ mapping.data.append(-mapping.data.size() & 4095, 0);
+ crashinfo->mappings[mapping.start_address] = mapping;
+ return;
+ }
+ }
+ // Didn't find a suitable existing mapping for the data. Create a new one.
+ CrashedProcess::Mapping mapping;
+ mapping.permissions = PF_R | PF_W;
+ mapping.start_address = addr & ~4095;
+ mapping.end_address =
+ (addr + data.size() + 4095) & ~4095;
+ mapping.data.assign(addr & 4095, 0).append(data);
+ mapping.data.append(-mapping.data.size() & 4095, 0);
+ crashinfo->mappings[mapping.start_address] = mapping;
+}
+
+static void
+AugmentMappings(const Options& options, CrashedProcess* crashinfo,
+ const MinidumpMemoryRange& full_file) {
+ // For each thread, find the memory mapping that matches the thread's stack.
+ // Then adjust the mapping to include the stack dump.
+ for (unsigned i = 0; i < crashinfo->threads.size(); ++i) {
+ const CrashedProcess::Thread& thread = crashinfo->threads[i];
+ AddDataToMapping(crashinfo,
+ string((char *)thread.stack, thread.stack_length),
+ thread.stack_addr);
+ }
+
+ // Create a new link map with information about DSOs. We move this map to
+ // the beginning of the address space, as this area should always be
+ // available.
+ static const uintptr_t start_addr = 4096;
+ string data;
+ struct r_debug debug = { 0 };
+ debug.r_version = crashinfo->debug.version;
+ debug.r_brk = (ElfW(Addr))crashinfo->debug.brk;
+ debug.r_state = r_debug::RT_CONSISTENT;
+ debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase;
+ debug.r_map = crashinfo->debug.dso_count > 0 ?
+ (struct link_map*)(start_addr + sizeof(debug)) : 0;
+ data.append((char*)&debug, sizeof(debug));
+
+ struct link_map* prev = 0;
+ for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin();
+ iter != crashinfo->link_map.end();
+ ++iter) {
+ struct link_map link_map = { 0 };
+ link_map.l_addr = (ElfW(Addr))iter->addr;
+ link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map));
+ link_map.l_ld = (ElfW(Dyn)*)iter->ld;
+ link_map.l_prev = prev;
+ prev = (struct link_map*)(start_addr + data.size());
+ string filename = full_file.GetAsciiMDString(iter->name);
+
+ // Look up signature for this filename. If available, change filename
+ // to point to GUID, instead.
+ std::map<uintptr_t, CrashedProcess::Signature>::const_iterator sig =
+ crashinfo->signatures.find((uintptr_t)iter->addr);
+ if (sig != crashinfo->signatures.end()) {
+ // At this point, we have:
+ // old_filename: The path as found via SONAME (e.g. /lib/libpthread.so.0).
+ // sig_filename: The path on disk (e.g. /lib/libpthread-2.19.so).
+ const char* guid = sig->second.guid;
+ string sig_filename = sig->second.filename;
+ string old_filename = filename.empty() ? sig_filename : filename;
+ string new_filename;
+
+ // First set up the leading path. We assume dirname always ends with a
+ // trailing slash (as needed), so we won't be appending one manually.
+ if (options.so_basedir.empty()) {
+ string dirname;
+ if (options.use_filename) {
+ dirname = sig_filename;
+ } else {
+ dirname = old_filename;
+ }
+ size_t slash = dirname.find_last_of('/');
+ if (slash != string::npos) {
+ new_filename = dirname.substr(0, slash + 1);
+ }
+ } else {
+ new_filename = options.so_basedir;
+ }
+
+ // Insert the module ID if requested.
+ if (options.inc_guid &&
+ strcmp(guid, "00000000-0000-0000-0000-000000000000") != 0) {
+ new_filename += guid;
+ new_filename += "-";
+ }
+
+ // Decide whether we use the filename or the SONAME (where the SONAME tends
+ // to be a symlink to the actual file).
+ new_filename += google_breakpad::BaseName(
+ options.use_filename ? sig_filename : old_filename);
+
+ if (filename != new_filename) {
+ if (options.verbose) {
+ fprintf(stderr, "0x%" PRIx64": rewriting mapping \"%s\" to \"%s\"\n",
+ static_cast<uint64_t>(link_map.l_addr),
+ filename.c_str(), new_filename.c_str());
+ }
+ filename = new_filename;
+ }
+ }
+
+ if (std::distance(iter, crashinfo->link_map.end()) == 1) {
+ link_map.l_next = 0;
+ } else {
+ link_map.l_next = (struct link_map*)(start_addr + data.size() +
+ sizeof(link_map) +
+ ((filename.size() + 8) & ~7));
+ }
+ data.append((char*)&link_map, sizeof(link_map));
+ data.append(filename);
+ data.append(8 - (filename.size() & 7), 0);
+ }
+ AddDataToMapping(crashinfo, data, start_addr);
+
+ // Map the page containing the _DYNAMIC array
+ if (!crashinfo->dynamic_data.empty()) {
+ // Make _DYNAMIC DT_DEBUG entry point to our link map
+ for (int i = 0;; ++i) {
+ ElfW(Dyn) dyn;
+ if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) {
+ no_dt_debug:
+ if (options.verbose) {
+ fprintf(stderr, "No DT_DEBUG entry found\n");
+ }
+ return;
+ }
+ memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn),
+ sizeof(dyn));
+ if (dyn.d_tag == DT_DEBUG) {
+ crashinfo->dynamic_data.replace(i*sizeof(dyn) +
+ offsetof(ElfW(Dyn), d_un.d_ptr),
+ sizeof(start_addr),
+ (char*)&start_addr, sizeof(start_addr));
+ break;
+ } else if (dyn.d_tag == DT_NULL) {
+ goto no_dt_debug;
+ }
+ }
+ AddDataToMapping(crashinfo, crashinfo->dynamic_data,
+ (uintptr_t)crashinfo->debug.dynamic);
+ }
+}
+
+int
+main(int argc, const char* argv[]) {
+ Options options;
+ SetupOptions(argc, argv, &options);
+
+ MemoryMappedFile mapped_file(options.minidump_path.c_str(), 0);
+ if (!mapped_file.data()) {
+ fprintf(stderr, "Failed to mmap dump file: %s: %s\n",
+ options.minidump_path.c_str(), strerror(errno));
+ return 1;
+ }
+
+ MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
+
+ const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
+
+ CrashedProcess crashinfo;
+
+ // Always check the system info first, as that allows us to tell whether
+ // this is a minidump file that is compatible with our converter.
+ bool ok = false;
+ for (unsigned i = 0; i < header->stream_count; ++i) {
+ const MDRawDirectory* dirent =
+ dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
+ switch (dirent->stream_type) {
+ case MD_SYSTEM_INFO_STREAM:
+ ParseSystemInfo(options, &crashinfo, dump.Subrange(dirent->location),
+ dump);
+ ok = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!ok) {
+ fprintf(stderr, "Cannot determine input file format.\n");
+ exit(1);
+ }
+
+ for (unsigned i = 0; i < header->stream_count; ++i) {
+ const MDRawDirectory* dirent =
+ dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
+ switch (dirent->stream_type) {
+ case MD_THREAD_LIST_STREAM:
+ ParseThreadList(options, &crashinfo, dump.Subrange(dirent->location),
+ dump);
+ break;
+ case MD_LINUX_CPU_INFO:
+ ParseCPUInfo(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_PROC_STATUS:
+ ParseProcessStatus(options, &crashinfo,
+ dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_LSB_RELEASE:
+ ParseLSBRelease(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_ENVIRON:
+ ParseEnvironment(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_MAPS:
+ ParseMaps(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_AUXV:
+ ParseAuxVector(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_CMD_LINE:
+ ParseCmdLine(options, &crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_DSO_DEBUG:
+ ParseDSODebugInfo(options, &crashinfo, dump.Subrange(dirent->location),
+ dump);
+ break;
+ case MD_EXCEPTION_STREAM:
+ ParseExceptionStream(options, &crashinfo,
+ dump.Subrange(dirent->location));
+ break;
+ case MD_MODULE_LIST_STREAM:
+ ParseModuleStream(options, &crashinfo, dump.Subrange(dirent->location),
+ dump);
+ break;
+ default:
+ if (options.verbose)
+ fprintf(stderr, "Skipping %x\n", dirent->stream_type);
+ }
+ }
+
+ AugmentMappings(options, &crashinfo, dump);
+
+ // Write the ELF header. The file will look like:
+ // ELF header
+ // Phdr for the PT_NOTE
+ // Phdr for each of the thread stacks
+ // PT_NOTE
+ // each of the thread stacks
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(Ehdr));
+ ehdr.e_ident[0] = ELFMAG0;
+ ehdr.e_ident[1] = ELFMAG1;
+ ehdr.e_ident[2] = ELFMAG2;
+ ehdr.e_ident[3] = ELFMAG3;
+ ehdr.e_ident[4] = ELF_CLASS;
+ ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
+ ehdr.e_ident[6] = EV_CURRENT;
+ ehdr.e_type = ET_CORE;
+ ehdr.e_machine = ELF_ARCH;
+ ehdr.e_version = EV_CURRENT;
+ ehdr.e_phoff = sizeof(Ehdr);
+ ehdr.e_ehsize = sizeof(Ehdr);
+ ehdr.e_phentsize= sizeof(Phdr);
+ ehdr.e_phnum = 1 + // PT_NOTE
+ crashinfo.mappings.size(); // memory mappings
+ ehdr.e_shentsize= sizeof(Shdr);
+ if (!writea(options.out_fd, &ehdr, sizeof(Ehdr)))
+ return 1;
+
+ size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr);
+ size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
+ // sizeof(Nhdr) + 8 + sizeof(user) +
+ sizeof(Nhdr) + 8 + crashinfo.auxv_length +
+ crashinfo.threads.size() * (
+ (sizeof(Nhdr) + 8 + sizeof(prstatus))
+#if defined(__i386__) || defined(__x86_64__)
+ + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct)
+#endif
+#if defined(__i386__)
+ + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
+#endif
+ );
+
+ Phdr phdr;
+ memset(&phdr, 0, sizeof(Phdr));
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_filesz = filesz;
+ if (!writea(options.out_fd, &phdr, sizeof(phdr)))
+ return 1;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_align = 4096;
+ size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
+ if (note_align == phdr.p_align)
+ note_align = 0;
+ offset += note_align;
+
+ for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
+ crashinfo.mappings.begin();
+ iter != crashinfo.mappings.end(); ++iter) {
+ const CrashedProcess::Mapping& mapping = iter->second;
+ if (mapping.permissions == 0xFFFFFFFF) {
+ // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to
+ // MD_LINUX_MAPS). It lacks some of the information that we would like
+ // to include.
+ phdr.p_flags = PF_R;
+ } else {
+ phdr.p_flags = mapping.permissions;
+ }
+ phdr.p_vaddr = mapping.start_address;
+ phdr.p_memsz = mapping.end_address - mapping.start_address;
+ if (mapping.data.size()) {
+ offset += filesz;
+ filesz = mapping.data.size();
+ phdr.p_filesz = mapping.data.size();
+ phdr.p_offset = offset;
+ } else {
+ phdr.p_filesz = 0;
+ phdr.p_offset = 0;
+ }
+ if (!writea(options.out_fd, &phdr, sizeof(phdr)))
+ return 1;
+ }
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(prpsinfo);
+ nhdr.n_type = NT_PRPSINFO;
+ if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) ||
+ !writea(options.out_fd, "CORE\0\0\0\0", 8) ||
+ !writea(options.out_fd, &crashinfo.prps, sizeof(prpsinfo))) {
+ return 1;
+ }
+
+ nhdr.n_descsz = crashinfo.auxv_length;
+ nhdr.n_type = NT_AUXV;
+ if (!writea(options.out_fd, &nhdr, sizeof(nhdr)) ||
+ !writea(options.out_fd, "CORE\0\0\0\0", 8) ||
+ !writea(options.out_fd, crashinfo.auxv, crashinfo.auxv_length)) {
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ if (crashinfo.threads[i].tid == crashinfo.crashing_tid) {
+ WriteThread(options, crashinfo.threads[i], crashinfo.fatal_signal);
+ break;
+ }
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
+ WriteThread(options, crashinfo.threads[i], 0);
+ }
+
+ if (note_align) {
+ google_breakpad::scoped_array<char> scratch(new char[note_align]);
+ memset(scratch.get(), 0, note_align);
+ if (!writea(options.out_fd, scratch.get(), note_align))
+ return 1;
+ }
+
+ for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
+ crashinfo.mappings.begin();
+ iter != crashinfo.mappings.end(); ++iter) {
+ const CrashedProcess::Mapping& mapping = iter->second;
+ if (mapping.data.size()) {
+ if (!writea(options.out_fd, mapping.data.c_str(), mapping.data.size()))
+ return 1;
+ }
+ }
+
+ if (options.out_fd != STDOUT_FILENO) {
+ close(options.out_fd);
+ }
+
+ return 0;
+}