summaryrefslogtreecommitdiffstats
path: root/breakpad/linux/minidump-2-core.cc
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 03:37:45 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-22 03:37:45 +0000
commit9a5d2a52a52bc167014d1e375208870278f451e0 (patch)
treef5b64315fc48c8b6e27089445d95c6f0ee189c50 /breakpad/linux/minidump-2-core.cc
parent597725dc3391c9728461a2f93e1420bc8d386923 (diff)
downloadchromium_src-9a5d2a52a52bc167014d1e375208870278f451e0.zip
chromium_src-9a5d2a52a52bc167014d1e375208870278f451e0.tar.gz
chromium_src-9a5d2a52a52bc167014d1e375208870278f451e0.tar.bz2
Linux: add Breakpad support
This commits a rewrite of the Breakpad Linux client. The old code: * Had a number of plain bugs in it, but those could just have been fixed. * Allocated memory from the heap, which is a no go. * Made libc calls which can enter the dynamic linker - another source of crashes. * Didn't understand some of the tricks needed, like clone() via libc will write to random areas of memory because it assumes that it's only called from libpthread Additionally, we had one more requirement which meant changing the interface: * We need to be able to crash dump the renderers from the browser process. And that last one really needed a rewrite. We intend to try and upstream this new code into Breakpad. The new Breakpad design works like this: When a renderer crashes, a signal handler runs on an alternative stack and collects information about the registers of the thread before the crash. Then we enter Chromium specific code an send a datagram message to a magic file descriptor (4) containing: * the registers and tid of the crashing thread * the active URL * a file descriptor to a socket * a CREDENTIALS structure giving the PID of the renderer. On the other end of the socket is an object on the IO thread (render_crash_handler_host_linux.cc) which reads and parses the datagram. The CREDENTIALS structure is validated by the kernel, so the renderer can't lie about it's PID and try and get the browser to crash dump the wrong process. The browser then ptraces the renderer and extracts all the needed information to write a minidump to a temp file. Then we write a byte to the file descriptor which the renderer gave the browser in the datagram and that's the signal to the renderer to finish dying. It dies by sending itself the same signal which trigger the crash dump in the first place, so it will appear to crash as normal as far as kernel core dumps and waitpid are concerned. The browser then constucts a MIME message in a temp file for upload to the crash service. We then fork out to /usr/bin/wget to actually do the upload (since Debian numbers suggest that 99.8% of users have wget installed.) A second forked child unlinks the temp files once wget has completed. For a browser crash, everything works pretty much the same except that the datagram step is omitted and we clone() off a process to ptrace ourselves and write the minidump. This code is only enabled in Chrome branded builds. Stub source files are substituted in the case of a Chromium build. http://codereview.chromium.org/115526 BUG=9646,10772 TEST=Build a Chrome branded binary. Send SEGV to a renderer and verify that wget output appears on stderr. Send a SEGV to the main binary and verify the same. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16719 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'breakpad/linux/minidump-2-core.cc')
-rw-r--r--breakpad/linux/minidump-2-core.cc601
1 files changed, 601 insertions, 0 deletions
diff --git a/breakpad/linux/minidump-2-core.cc b/breakpad/linux/minidump-2-core.cc
new file mode 100644
index 0000000..095ae5f
--- /dev/null
+++ b/breakpad/linux/minidump-2-core.cc
@@ -0,0 +1,601 @@
+// 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/
+//
+// Usage: minidump-2-core 1234.dmp > core
+
+#include <vector>
+
+#include <stdio.h>
+
+#include <elf.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/user.h>
+#include <sys/mman.h>
+
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/common/minidump_cpu_x86.h"
+#include "breakpad/linux/minidump_format_linux.h"
+
+#if __WORDSIZE == 64
+ #define ELF_CLASS ELFCLASS64
+ #define Ehdr Elf64_Ehdr
+ #define Phdr Elf64_Phdr
+ #define Shdr Elf64_Shdr
+ #define Nhdr Elf64_Nhdr
+ #define auxv_t Elf64_auxv_t
+#else
+ #define ELF_CLASS ELFCLASS32
+ #define Ehdr Elf32_Ehdr
+ #define Phdr Elf32_Phdr
+ #define Shdr Elf32_Shdr
+ #define Nhdr Elf32_Nhdr
+ #define auxv_t Elf32_auxv_t
+#endif
+
+
+#if defined(__x86_64__)
+ #define ELF_ARCH EM_X86_64
+#elif defined(__i386__)
+ #define ELF_ARCH EM_386
+#elif defined(__ARM_ARCH_3__)
+ #define ELF_ARCH EM_ARM
+#elif defined(__mips__)
+ #define ELF_ARCH EM_MIPS
+#endif
+
+static int usage(const char* argv0) {
+ fprintf(stderr, "Usage: %s <minidump file>\n", argv0);
+ return 1;
+}
+
+// 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;
+}
+
+// A range of a mmaped file.
+class MMappedRange {
+ public:
+ MMappedRange(const void* data, size_t length)
+ : data_(reinterpret_cast<const uint8_t*>(data)),
+ length_(length) {
+ }
+
+ // Get an object of |length| bytes at |offset| and return a pointer to it
+ // unless it's out of bounds.
+ const void* GetObject(size_t offset, size_t length) {
+ if (offset + length < offset)
+ return NULL;
+ if (offset + length > length_)
+ return NULL;
+ return data_ + offset;
+ }
+
+ // Get element |index| of an array of objects of length |length| starting at
+ // |offset| bytes. Return NULL if out of bounds.
+ const void* GetArrayElement(size_t offset, size_t length, unsigned index) {
+ const size_t element_offset = offset + index * length;
+ return GetObject(element_offset, length);
+ }
+
+ // Return a new range which is a subset of this range.
+ MMappedRange Subrange(const MDLocationDescriptor& location) const {
+ if (location.rva > length_ ||
+ location.rva + location.data_size < location.rva ||
+ location.rva + location.data_size > length_) {
+ return MMappedRange(NULL, 0);
+ }
+
+ return MMappedRange(data_ + location.rva, location.data_size);
+ }
+
+ const uint8_t* data() const { return data_; }
+ size_t length() const { return length_; }
+
+ private:
+ const uint8_t* const data_;
+ const size_t length_;
+};
+
+/* 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';
+ }
+
+ struct Mapping {
+ uint64_t start_address, end_address;
+ };
+ std::vector<Mapping> mappings;
+
+ pid_t crashing_tid;
+ int fatal_signal;
+
+ struct Thread {
+ pid_t tid;
+ user_regs_struct regs;
+ user_fpregs_struct fpregs;
+ user_fpxregs_struct fpxregs;
+ 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;
+};
+
+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;
+}
+
+#if defined(__i386__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
+ const MDRawContextX86* rawregs =
+ (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86));
+
+ 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);
+}
+#else
+#error "This code has not been ported to your platform yet"
+#endif
+
+static void
+ParseThreadList(CrashedProcess* crashinfo, MMappedRange range,
+ const MMappedRange& full_file) {
+ const uint32_t num_threads =
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
+ for (unsigned i = 0; i < num_threads; ++i) {
+ CrashedProcess::Thread thread;
+ memset(&thread, 0, sizeof(thread));
+ const MDRawThread* rawthread =
+ (MDRawThread*) range.GetArrayElement(sizeof(uint32_t),
+ sizeof(MDRawThread), i);
+ thread.tid = rawthread->thread_id;
+ thread.stack_addr = rawthread->stack.start_of_memory_range;
+ MMappedRange 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
+ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) {
+ crashinfo->auxv = range.data();
+ crashinfo->auxv_length = range.length();
+}
+
+static void
+ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) {
+ const char* cmdline = (const char*) range.data();
+ for (size_t i = 0; i < range.length(); ++i) {
+ if (cmdline[i] == 0) {
+ 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);
+ const char* binary_name = strrchr(cmdline, '/');
+ if (binary_name) {
+ binary_name++;
+ const unsigned len = strlen(binary_name);
+ memcpy(crashinfo->prps.pr_fname, binary_name,
+ len > fname_len ? fname_len : len);
+ } else {
+ memcpy(crashinfo->prps.pr_fname, cmdline,
+ i > fname_len ? fname_len : i);
+ }
+
+ const unsigned len = range.length() > args_len ?
+ args_len : range.length();
+ memcpy(crashinfo->prps.pr_psargs, cmdline, len);
+ for (unsigned i = 0; i < len; ++i) {
+ if (crashinfo->prps.pr_psargs[i] == 0)
+ crashinfo->prps.pr_psargs[i] = ' ';
+ }
+ }
+ }
+}
+
+static void
+ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) {
+ const MDRawExceptionStream* exp =
+ (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream));
+ crashinfo->crashing_tid = exp->thread_id;
+ crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
+}
+
+static bool
+WriteThread(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;
+ memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(struct prstatus);
+ nhdr.n_type = NT_PRSTATUS;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &pr, sizeof(struct prstatus))) {
+ return false;
+ }
+
+ nhdr.n_descsz = sizeof(user_fpregs_struct);
+ nhdr.n_type = NT_FPREGSET;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) {
+ return false;
+ }
+
+ nhdr.n_descsz = sizeof(user_fpxregs_struct);
+ nhdr.n_type = NT_PRXFPREG;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "LINUX\0\0\0", 8) ||
+ !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
+ return false;
+ }
+
+ return true;
+}
+
+static void
+ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) {
+ const uint32_t num_mappings =
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
+ for (unsigned i = 0; i < num_mappings; ++i) {
+ CrashedProcess::Mapping mapping;
+ const MDRawModule* rawmodule =
+ (MDRawModule*) range.GetArrayElement(sizeof(uint32_t),
+ sizeof(MDRawModule), i);
+ mapping.start_address = rawmodule->base_of_image;
+ mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
+
+ crashinfo->mappings.push_back(mapping);
+ }
+}
+
+int
+main(int argc, char** argv) {
+ if (argc != 2)
+ return usage(argv[0]);
+
+ const int fd = open(argv[1], O_RDONLY);
+ if (fd < 0)
+ return usage(argv[0]);
+
+ struct stat st;
+ fstat(fd, &st);
+
+ const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+ if (bytes == MAP_FAILED) {
+ perror("Failed to mmap dump file");
+ return 1;
+ }
+
+ MMappedRange dump(bytes, st.st_size);
+
+ const MDRawHeader* header =
+ (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader));
+
+ CrashedProcess crashinfo;
+
+ for (unsigned i = 0; i < header->stream_count; ++i) {
+ const MDRawDirectory* dirent =
+ (const MDRawDirectory*) dump.GetArrayElement(
+ header->stream_directory_rva, sizeof(MDRawDirectory), i);
+ switch (dirent->stream_type) {
+ case MD_THREAD_LIST_STREAM:
+ ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
+ break;
+ case MD_LINUX_AUXV:
+ ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_CMD_LINE:
+ ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_EXCEPTION_STREAM:
+ ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_MODULE_LIST_STREAM:
+ ParseModuleStream(&crashinfo, dump.Subrange(dirent->location));
+ default:
+ fprintf(stderr, "Skipping %x\n", dirent->stream_type);
+ }
+ }
+
+ // 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 + crashinfo.threads.size() + crashinfo.mappings.size();
+ ehdr.e_shentsize= sizeof(Shdr);
+ if (!writea(1, &ehdr, sizeof(Ehdr)))
+ return 1;
+
+ size_t offset = sizeof(Ehdr) +
+ (1 + crashinfo.threads.size() +
+ crashinfo.mappings.size()) * 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)) +
+ sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) +
+ sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct));
+
+ Phdr phdr;
+ memset(&phdr, 0, sizeof(Phdr));
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_filesz = filesz;
+ if (!writea(1, &phdr, sizeof(phdr)))
+ return 1;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_align = getpagesize();
+ 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 (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ const CrashedProcess::Thread& thread = crashinfo.threads[i];
+ offset += filesz;
+ filesz = thread.stack_length;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = thread.stack_addr;
+ phdr.p_filesz = phdr.p_memsz = filesz;
+ phdr.p_flags = PF_R | PF_W;
+ if (!writea(1, &phdr, sizeof(phdr)))
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) {
+ const CrashedProcess::Mapping& mapping = crashinfo.mappings[i];
+ phdr.p_offset = 0;
+ phdr.p_vaddr = mapping.start_address;
+ phdr.p_filesz = 0;
+ phdr.p_flags = PF_R;
+ phdr.p_memsz = mapping.end_address - mapping.start_address;
+ if (!writea(1, &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(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &crashinfo.prps, sizeof(prpsinfo))) {
+ return 1;
+ }
+
+ nhdr.n_descsz = crashinfo.auxv_length;
+ nhdr.n_type = NT_AUXV;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &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(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(crashinfo.threads[i], 0);
+ }
+
+ if (note_align) {
+ char scratch[note_align];
+ memset(scratch, 0, sizeof(scratch));
+ if (!writea(1, scratch, sizeof(scratch)))
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ const CrashedProcess::Thread& thread = crashinfo.threads[i];
+ if (!writea(1, thread.stack, thread.stack_length))
+ return 1;
+ }
+
+ munmap(const_cast<void*>(bytes), st.st_size);
+
+ return 0;
+}