minidump-2-core.cc revision af0cd7cb05ea70d6c19ec83235b7a87e735d2b27
1// Copyright (c) 2009, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// Converts a minidump file to a core file which gdb can read.
31// Large parts lifted from the userspace core dumper:
32//   http://code.google.com/p/google-coredumper/
33//
34// Usage: minidump-2-core [-v] 1234.dmp > core
35
36#include <elf.h>
37#include <errno.h>
38#include <link.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/user.h>
43#include <unistd.h>
44
45#include <map>
46#include <string>
47#include <vector>
48
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"
54
55#if __WORDSIZE == 64
56  #define ELF_CLASS ELFCLASS64
57#else
58  #define ELF_CLASS ELFCLASS32
59#endif
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)
65
66
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
75#endif
76
77#if defined(__arm__)
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.
81typedef user_regs user_regs_struct;
82#endif
83
84using google_breakpad::MemoryMappedFile;
85using google_breakpad::MinidumpMemoryRange;
86
87static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
88static bool verbose;
89static std::string g_custom_so_basedir;
90
91static int usage(const char* argv0) {
92  fprintf(stderr, "Usage: %s [-v] <minidump file>\n", argv0);
93  return 1;
94}
95
96// Write all of the given buffer, handling short writes and EINTR. Return true
97// iff successful.
98static bool
99writea(int fd, const void* idata, size_t length) {
100  const uint8_t* data = (const uint8_t*) idata;
101
102  size_t done = 0;
103  while (done < length) {
104    ssize_t r;
105    do {
106      r = write(fd, data + done, length - done);
107    } while (r == -1 && errno == EINTR);
108
109    if (r < 1)
110      return false;
111    done += r;
112  }
113
114  return true;
115}
116
117/* Dynamically determines the byte sex of the system. Returns non-zero
118 * for big-endian machines.
119 */
120static inline int sex() {
121  int probe = 1;
122  return !*(char *)&probe;
123}
124
125typedef struct elf_timeval {    /* Time value with microsecond resolution    */
126  long tv_sec;                  /* Seconds                                   */
127  long tv_usec;                 /* Microseconds                              */
128} elf_timeval;
129
130typedef 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                                     */
134} elf_siginfo;
135
136typedef 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      */
151} prstatus;
152
153typedef 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                                  */
162#else
163  uint16_t       pr_uid;        /* User ID                                   */
164  uint16_t       pr_gid;        /* Group ID                                  */
165#endif
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                  */
172} prpsinfo;
173
174// We parse the minidump file and keep the parsed information in this structure
175struct CrashedProcess {
176  CrashedProcess()
177      : crashing_tid(-1),
178        auxv(NULL),
179        auxv_length(0) {
180    memset(&prps, 0, sizeof(prps));
181    prps.pr_sname = 'R';
182    memset(&debug, 0, sizeof(debug));
183  }
184
185  struct Mapping {
186    Mapping()
187      : permissions(0xFFFFFFFF),
188        start_address(0),
189        end_address(0),
190        offset(0) {
191    }
192
193    uint32_t permissions;
194    uint64_t start_address, end_address, offset;
195    std::string filename;
196    std::string data;
197  };
198  std::map<uint64_t, Mapping> mappings;
199
200  pid_t crashing_tid;
201  int fatal_signal;
202
203  struct Thread {
204    pid_t tid;
205    user_regs_struct regs;
206#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
207    user_fpregs_struct fpregs;
208#endif
209#if defined(__i386__)
210    user_fpxregs_struct fpxregs;
211#endif
212    uintptr_t stack_addr;
213    const uint8_t* stack;
214    size_t stack_length;
215  };
216  std::vector<Thread> threads;
217
218  const uint8_t* auxv;
219  size_t auxv_length;
220
221  prpsinfo prps;
222
223  std::map<uintptr_t, std::string> signatures;
224
225  std::string dynamic_data;
226  MDRawDebug debug;
227  std::vector<MDRawLinkMap> link_map;
228};
229
230#if defined(__i386__)
231static uint32_t
232U32(const uint8_t* data) {
233  uint32_t v;
234  memcpy(&v, data, sizeof(v));
235  return v;
236}
237
238static uint16_t
239U16(const uint8_t* data) {
240  uint16_t v;
241  memcpy(&v, data, sizeof(v));
242  return v;
243}
244
245static void
246ParseThreadRegisters(CrashedProcess::Thread* thread,
247                     const MinidumpMemoryRange& range) {
248  const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0);
249
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;
267
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,
276         10 * 8);
277
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);
289}
290#elif defined(__x86_64__)
291static void
292ParseThreadRegisters(CrashedProcess::Thread* thread,
293                     const MinidumpMemoryRange& range) {
294  const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0);
295
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;
323
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);
334}
335#elif defined(__arm__)
336static void
337ParseThreadRegisters(CrashedProcess::Thread* thread,
338                     const MinidumpMemoryRange& range) {
339  const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0);
340
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];
357
358  thread->regs.uregs[16] = rawregs->cpsr;
359  thread->regs.uregs[17] = 0;  // what is ORIG_r0 exactly?
360}
361#elif defined(__mips__)
362static void
363ParseThreadRegisters(CrashedProcess::Thread* thread,
364                     const MinidumpMemoryRange& range) {
365  const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0);
366
367  for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
368    thread->regs.regs[i] = rawregs->iregs[i];
369
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;
376
377  for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
378    thread->fpregs.regs[i] = rawregs->float_save.regs[i];
379
380  thread->fpregs.fpcsr = rawregs->float_save.fpcsr;
381  thread->fpregs.fir = rawregs->float_save.fir;
382}
383#else
384#error "This code has not been ported to your platform yet"
385#endif
386
387static void
388ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
389                const MinidumpMemoryRange& full_file) {
390  const uint32_t num_threads = *range.GetData<uint32_t>(0);
391  if (verbose) {
392    fprintf(stderr,
393            "MD_THREAD_LIST_STREAM:\n"
394            "Found %d threads\n"
395            "\n\n",
396            num_threads);
397  }
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;
409
410    ParseThreadRegisters(&thread,
411                         full_file.Subrange(rawthread->thread_context));
412
413    crashinfo->threads.push_back(thread);
414  }
415}
416
417static void
418ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
419                const MinidumpMemoryRange& full_file) {
420  const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
421  if (!sysinfo) {
422    fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
423    _exit(1);
424  }
425#if defined(__i386__)
426  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
427    fprintf(stderr,
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" : "");
431    _exit(1);
432  }
433#elif defined(__x86_64__)
434  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
435    fprintf(stderr,
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" : "");
439    _exit(1);
440  }
441#elif defined(__arm__)
442  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
443    fprintf(stderr,
444            "This version of minidump-2-core only supports ARM (32bit).\n");
445    _exit(1);
446  }
447#elif defined(__mips__)
448  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
449    fprintf(stderr,
450            "This version of minidump-2-core only supports mips (32bit).\n");
451    _exit(1);
452  }
453#else
454#error "This code has not been ported to your platform yet"
455#endif
456  if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
457              "Linux") &&
458      sysinfo->platform_id != MD_OS_NACL) {
459    fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n");
460    _exit(1);
461  }
462
463  if (verbose) {
464    fprintf(stderr,
465            "MD_SYSTEM_INFO_STREAM:\n"
466            "Architecture: %s\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
472            ? "i386"
473            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
474            ? "x86-64"
475            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
476            ? "ARM"
477            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
478            ? "MIPS"
479            : "???",
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);
487      const char *nul =
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);
493      fputs("\n", stderr);
494    }
495    fprintf(stderr, "OS: %s\n",
496            full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
497    fputs("\n\n", stderr);
498  }
499}
500
501static void
502ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
503  if (verbose) {
504    fputs("MD_LINUX_CPU_INFO:\n", stderr);
505    fwrite(range.data(), range.length(), 1, stderr);
506    fputs("\n\n\n", stderr);
507  }
508}
509
510static void
511ParseProcessStatus(CrashedProcess* crashinfo,
512                   const MinidumpMemoryRange& range) {
513  if (verbose) {
514    fputs("MD_LINUX_PROC_STATUS:\n", stderr);
515    fwrite(range.data(), range.length(), 1, stderr);
516    fputs("\n\n", stderr);
517  }
518}
519
520static void
521ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
522  if (verbose) {
523    fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
524    fwrite(range.data(), range.length(), 1, stderr);
525    fputs("\n\n", stderr);
526  }
527}
528
529static void
530ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
531  if (verbose) {
532    fputs("MD_LINUX_MAPS:\n", stderr);
533    fwrite(range.data(), range.length(), 1, stderr);
534  }
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;
552      }
553      if (strchr(permissions, 'w')) {
554        mapping.permissions |= PF_W;
555      }
556      if (strchr(permissions, 'x')) {
557        mapping.permissions |= PF_X;
558      }
559      mapping.start_address = start;
560      mapping.end_address = stop;
561      mapping.offset = offset;
562      if (filename) {
563        mapping.filename = filename;
564      }
565      crashinfo->mappings[mapping.start_address] = mapping;
566    }
567    free(permissions);
568    free(filename);
569  }
570  if (verbose) {
571    fputs("\n\n\n", stderr);
572  }
573}
574
575static void
576ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
577  if (verbose) {
578    fputs("MD_LINUX_ENVIRON:\n", stderr);
579    char* env = new char[range.length()];
580    memcpy(env, range.data(), range.length());
581    int nul_count = 0;
582    for (char *ptr = env;;) {
583      ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env));
584      if (!ptr) {
585        break;
586      }
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
593          // NUL bytes.
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);
599          goto env_corrupted;
600        }
601      } else {
602        nul_count = 0;
603      }
604      *ptr = '\n';
605    }
606    fwrite(env, range.length(), 1, stderr);
607  env_corrupted:
608    delete[] env;
609    fputs("\n\n\n", stderr);
610  }
611}
612
613static void
614ParseAuxVector(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
620    // address.
621    char addresses[18];
622    memcpy(addresses, range.data(), 17);
623    addresses[17] = '\000';
624    if (strspn(addresses, "0123456789abcdef-") == 17) {
625      ParseMaps(crashinfo, range);
626      return;
627    }
628  }
629
630  crashinfo->auxv = range.data();
631  crashinfo->auxv_length = range.length();
632}
633
634static void
635ParseCmdLine(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();
640  if (verbose) {
641    fputs("MD_LINUX_CMD_LINE:\n", stderr);
642    unsigned i = 0;
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);
652        i = j + 1;
653      }
654    }
655    fputs("\n\n", stderr);
656  }
657
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);
670
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] = ' ';
676      }
677      break;
678    }
679  }
680}
681
682static void
683ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
684                  const MinidumpMemoryRange& full_file) {
685  const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
686  if (!debug) {
687    return;
688  }
689  if (verbose) {
690    fprintf(stderr,
691            "MD_LINUX_DSO_DEBUG:\n"
692            "Version: %d\n"
693            "Number of DSOs: %d\n"
694            "Brk handler: %p\n"
695            "Dynamic loader at: %p\n"
696            "_DYNAMIC: %p\n",
697            debug->version,
698            debug->dso_count,
699            debug->brk,
700            debug->ldbase,
701            debug->dynamic);
702  }
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));
708  }
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);
713      if (link_map) {
714        if (verbose) {
715          fprintf(stderr,
716                  "#%03d: %p, %p, \"%s\"\n",
717                  i, link_map->addr, link_map->ld,
718                  full_file.GetAsciiMDString(link_map->name).c_str());
719        }
720        crashinfo->link_map.push_back(*link_map);
721      }
722    }
723  }
724  if (verbose) {
725    fputs("\n\n", stderr);
726  }
727}
728
729static void
730ParseExceptionStream(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;
735}
736
737static bool
738WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
739  struct prstatus pr;
740  memset(&pr, 0, sizeof(pr));
741
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));
746
747  Nhdr nhdr;
748  memset(&nhdr, 0, sizeof(nhdr));
749  nhdr.n_namesz = 5;
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))) {
755    return false;
756  }
757
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))) {
764    return false;
765  }
766#endif
767
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))) {
774    return false;
775  }
776#endif
777
778  return true;
779}
780
781static void
782ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
783                  const MinidumpMemoryRange& full_file) {
784  if (verbose) {
785    fputs("MD_MODULE_LIST_STREAM:\n", stderr);
786  }
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;
794
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;
800    }
801
802    const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
803        full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
804    char guid[40];
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")) {
818      std::string prefix;
819      if (!g_custom_so_basedir.empty())
820        prefix = g_custom_so_basedir;
821      else
822        prefix = std::string("/var/lib/breakpad/") + guid + "-" + basename;
823
824      crashinfo->signatures[rawmodule->base_of_image] = prefix + basename;
825    }
826
827    if (verbose) {
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());
833    }
834  }
835  if (verbose) {
836    fputs("\n\n", stderr);
837  }
838}
839
840static void
841AddDataToMapping(CrashedProcess* crashinfo, const std::string& data,
842                 uintptr_t addr) {
843  for (std::map<uint64_t, CrashedProcess::Mapping>::iterator
844         iter = crashinfo->mappings.begin();
845       iter != crashinfo->mappings.end();
846       ++iter) {
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;
865        }
866      }
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
870      // the data.
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;
875      return;
876    }
877  }
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;
887}
888
889static void
890AugmentMappings(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),
898                     thread.stack_addr);
899  }
900
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
903  // available.
904  static const uintptr_t start_addr = 4096;
905  std::string data;
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));
914
915  struct link_map* prev = 0;
916  for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin();
917       iter != crashinfo->link_map.end();
918       ++iter) {
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);
926
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;
933    }
934
935    if (std::distance(iter, crashinfo->link_map.end()) == 1) {
936      link_map.l_next = 0;
937    } else {
938      link_map.l_next = (struct link_map*)(start_addr + data.size() +
939                                           sizeof(link_map) +
940                                           ((filename.size() + 8) & ~7));
941    }
942    data.append((char*)&link_map, sizeof(link_map));
943    data.append(filename);
944    data.append(8 - (filename.size() & 7), 0);
945  }
946  AddDataToMapping(crashinfo, data, start_addr);
947
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) {
952      ElfW(Dyn) dyn;
953      if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) {
954      no_dt_debug:
955        if (verbose) {
956          fprintf(stderr, "No DT_DEBUG entry found\n");
957        }
958        return;
959      }
960      memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn),
961             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),
965                                       sizeof(start_addr),
966                                       (char*)&start_addr, sizeof(start_addr));
967        break;
968      } else if (dyn.d_tag == DT_NULL) {
969        goto no_dt_debug;
970      }
971    }
972    AddDataToMapping(crashinfo, crashinfo->dynamic_data,
973                     (uintptr_t)crashinfo->debug.dynamic);
974  }
975}
976
977int
978main(int argc, char** argv) {
979  int argi = 1;
980  while (argi < argc && argv[argi][0] == '-') {
981    if (!strcmp(argv[argi], "-v")) {
982      verbose = true;
983    } else if (!strcmp(argv[argi], "--sobasedir")) {
984      argi++;
985      if (argi >= argc) {
986        fprintf(stderr, "--sobasedir expects an argument.");
987        return usage(argv[0]);
988      }
989
990      g_custom_so_basedir = argv[argi];
991    } else {
992      return usage(argv[0]);
993    }
994    argi++;
995  }
996
997  if (argc != argi + 1)
998    return usage(argv[0]);
999
1000  MemoryMappedFile mapped_file(argv[argi]);
1001  if (!mapped_file.data()) {
1002    fprintf(stderr, "Failed to mmap dump file\n");
1003    return 1;
1004  }
1005
1006  MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
1007
1008  const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
1009
1010  CrashedProcess crashinfo;
1011
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.
1014  bool ok = false;
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);
1021        ok = true;
1022        break;
1023      default:
1024        break;
1025    }
1026  }
1027  if (!ok) {
1028    fprintf(stderr, "Cannot determine input file format.\n");
1029    _exit(1);
1030  }
1031
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);
1038        break;
1039      case MD_LINUX_CPU_INFO:
1040        ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location));
1041        break;
1042      case MD_LINUX_PROC_STATUS:
1043        ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location));
1044        break;
1045      case MD_LINUX_LSB_RELEASE:
1046        ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location));
1047        break;
1048      case MD_LINUX_ENVIRON:
1049        ParseEnvironment(&crashinfo, dump.Subrange(dirent->location));
1050        break;
1051      case MD_LINUX_MAPS:
1052        ParseMaps(&crashinfo, dump.Subrange(dirent->location));
1053        break;
1054      case MD_LINUX_AUXV:
1055        ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
1056        break;
1057      case MD_LINUX_CMD_LINE:
1058        ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
1059        break;
1060      case MD_LINUX_DSO_DEBUG:
1061        ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1062        break;
1063      case MD_EXCEPTION_STREAM:
1064        ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
1065        break;
1066      case MD_MODULE_LIST_STREAM:
1067        ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump);
1068        break;
1069      default:
1070        if (verbose)
1071          fprintf(stderr, "Skipping %x\n", dirent->stream_type);
1072    }
1073  }
1074
1075  AugmentMappings(&crashinfo, dump);
1076
1077  // Write the ELF header. The file will look like:
1078  //   ELF header
1079  //   Phdr for the PT_NOTE
1080  //   Phdr for each of the thread stacks
1081  //   PT_NOTE
1082  //   each of the thread stacks
1083  Ehdr ehdr;
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)))
1102    return 1;
1103
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)
1112#endif
1113#if defined(__i386__)
1114                   + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
1115#endif
1116                    );
1117
1118  Phdr phdr;
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)))
1124    return 1;
1125
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)
1130    note_align = 0;
1131  offset += note_align;
1132
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
1140      // to include.
1141      phdr.p_flags = PF_R;
1142    } else {
1143      phdr.p_flags = mapping.permissions;
1144    }
1145    phdr.p_vaddr = mapping.start_address;
1146    phdr.p_memsz = mapping.end_address - mapping.start_address;
1147    if (mapping.data.size()) {
1148      offset += filesz;
1149      filesz = mapping.data.size();
1150      phdr.p_filesz = mapping.data.size();
1151      phdr.p_offset = offset;
1152    } else {
1153      phdr.p_filesz = 0;
1154      phdr.p_offset = 0;
1155    }
1156    if (!writea(1, &phdr, sizeof(phdr)))
1157      return 1;
1158  }
1159
1160  Nhdr nhdr;
1161  memset(&nhdr, 0, sizeof(nhdr));
1162  nhdr.n_namesz = 5;
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))) {
1168    return 1;
1169  }
1170
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)) {
1176    return 1;
1177  }
1178
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);
1182      break;
1183    }
1184  }
1185
1186  for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1187    if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
1188      WriteThread(crashinfo.threads[i], 0);
1189  }
1190
1191  if (note_align) {
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))
1195      return 1;
1196  }
1197
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()))
1204        return 1;
1205    }
1206  }
1207
1208  return 0;
1209}
1210