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 <inttypes.h>
39#include <link.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <sys/user.h>
44#include <unistd.h>
45
46#include <map>
47#include <string>
48#include <vector>
49
50#include "common/linux/memory_mapped_file.h"
51#include "common/minidump_type_helper.h"
52#include "common/scoped_ptr.h"
53#include "google_breakpad/common/minidump_format.h"
54#include "third_party/lss/linux_syscall_support.h"
55#include "tools/linux/md2core/minidump_memory_range.h"
56
57#if __WORDSIZE == 64
58  #define ELF_CLASS ELFCLASS64
59#else
60  #define ELF_CLASS ELFCLASS32
61#endif
62#define Ehdr   ElfW(Ehdr)
63#define Phdr   ElfW(Phdr)
64#define Shdr   ElfW(Shdr)
65#define Nhdr   ElfW(Nhdr)
66#define auxv_t ElfW(auxv_t)
67
68
69#if defined(__x86_64__)
70  #define ELF_ARCH  EM_X86_64
71#elif defined(__i386__)
72  #define ELF_ARCH  EM_386
73#elif defined(__arm__)
74  #define ELF_ARCH  EM_ARM
75#elif defined(__mips__)
76  #define ELF_ARCH  EM_MIPS
77#endif
78
79#if defined(__arm__)
80// GLibc/ARM and Android/ARM both use 'user_regs' for the structure type
81// containing core registers, while they use 'user_regs_struct' on other
82// architectures. This file-local typedef simplifies the source code.
83typedef user_regs user_regs_struct;
84#endif
85
86using google_breakpad::MDTypeHelper;
87using google_breakpad::MemoryMappedFile;
88using google_breakpad::MinidumpMemoryRange;
89
90typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawDebug MDRawDebug;
91typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawLinkMap MDRawLinkMap;
92
93static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
94static bool verbose;
95static std::string g_custom_so_basedir;
96
97static int usage(const char* argv0) {
98  fprintf(stderr, "Usage: %s [-v] <minidump file>\n", argv0);
99  return 1;
100}
101
102// Write all of the given buffer, handling short writes and EINTR. Return true
103// iff successful.
104static bool
105writea(int fd, const void* idata, size_t length) {
106  const uint8_t* data = (const uint8_t*) idata;
107
108  size_t done = 0;
109  while (done < length) {
110    ssize_t r;
111    do {
112      r = write(fd, data + done, length - done);
113    } while (r == -1 && errno == EINTR);
114
115    if (r < 1)
116      return false;
117    done += r;
118  }
119
120  return true;
121}
122
123/* Dynamically determines the byte sex of the system. Returns non-zero
124 * for big-endian machines.
125 */
126static inline int sex() {
127  int probe = 1;
128  return !*(char *)&probe;
129}
130
131typedef struct elf_timeval {    /* Time value with microsecond resolution    */
132  long tv_sec;                  /* Seconds                                   */
133  long tv_usec;                 /* Microseconds                              */
134} elf_timeval;
135
136typedef struct elf_siginfo {    /* Information about signal (unused)         */
137  int32_t si_signo;             /* Signal number                             */
138  int32_t si_code;              /* Extra code                                */
139  int32_t si_errno;             /* Errno                                     */
140} elf_siginfo;
141
142typedef struct prstatus {       /* Information about thread; includes CPU reg*/
143  elf_siginfo    pr_info;       /* Info associated with signal               */
144  uint16_t       pr_cursig;     /* Current signal                            */
145  unsigned long  pr_sigpend;    /* Set of pending signals                    */
146  unsigned long  pr_sighold;    /* Set of held signals                       */
147  pid_t          pr_pid;        /* Process ID                                */
148  pid_t          pr_ppid;       /* Parent's process ID                       */
149  pid_t          pr_pgrp;       /* Group ID                                  */
150  pid_t          pr_sid;        /* Session ID                                */
151  elf_timeval    pr_utime;      /* User time                                 */
152  elf_timeval    pr_stime;      /* System time                               */
153  elf_timeval    pr_cutime;     /* Cumulative user time                      */
154  elf_timeval    pr_cstime;     /* Cumulative system time                    */
155  user_regs_struct pr_reg;      /* CPU registers                             */
156  uint32_t       pr_fpvalid;    /* True if math co-processor being used      */
157} prstatus;
158
159typedef struct prpsinfo {       /* Information about process                 */
160  unsigned char  pr_state;      /* Numeric process state                     */
161  char           pr_sname;      /* Char for pr_state                         */
162  unsigned char  pr_zomb;       /* Zombie                                    */
163  signed char    pr_nice;       /* Nice val                                  */
164  unsigned long  pr_flag;       /* Flags                                     */
165#if defined(__x86_64__) || defined(__mips__)
166  uint32_t       pr_uid;        /* User ID                                   */
167  uint32_t       pr_gid;        /* Group ID                                  */
168#else
169  uint16_t       pr_uid;        /* User ID                                   */
170  uint16_t       pr_gid;        /* Group ID                                  */
171#endif
172  pid_t          pr_pid;        /* Process ID                                */
173  pid_t          pr_ppid;       /* Parent's process ID                       */
174  pid_t          pr_pgrp;       /* Group ID                                  */
175  pid_t          pr_sid;        /* Session ID                                */
176  char           pr_fname[16];  /* Filename of executable                    */
177  char           pr_psargs[80]; /* Initial part of arg list                  */
178} prpsinfo;
179
180// We parse the minidump file and keep the parsed information in this structure
181struct CrashedProcess {
182  CrashedProcess()
183      : crashing_tid(-1),
184        auxv(NULL),
185        auxv_length(0) {
186    memset(&prps, 0, sizeof(prps));
187    prps.pr_sname = 'R';
188    memset(&debug, 0, sizeof(debug));
189  }
190
191  struct Mapping {
192    Mapping()
193      : permissions(0xFFFFFFFF),
194        start_address(0),
195        end_address(0),
196        offset(0) {
197    }
198
199    uint32_t permissions;
200    uint64_t start_address, end_address, offset;
201    std::string filename;
202    std::string data;
203  };
204  std::map<uint64_t, Mapping> mappings;
205
206  pid_t crashing_tid;
207  int fatal_signal;
208
209  struct Thread {
210    pid_t tid;
211    user_regs_struct regs;
212#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
213    user_fpregs_struct fpregs;
214#endif
215#if defined(__i386__)
216    user_fpxregs_struct fpxregs;
217#endif
218    uintptr_t stack_addr;
219    const uint8_t* stack;
220    size_t stack_length;
221  };
222  std::vector<Thread> threads;
223
224  const uint8_t* auxv;
225  size_t auxv_length;
226
227  prpsinfo prps;
228
229  std::map<uintptr_t, std::string> signatures;
230
231  std::string dynamic_data;
232  MDRawDebug debug;
233  std::vector<MDRawLinkMap> link_map;
234};
235
236#if defined(__i386__)
237static uint32_t
238U32(const uint8_t* data) {
239  uint32_t v;
240  memcpy(&v, data, sizeof(v));
241  return v;
242}
243
244static uint16_t
245U16(const uint8_t* data) {
246  uint16_t v;
247  memcpy(&v, data, sizeof(v));
248  return v;
249}
250
251static void
252ParseThreadRegisters(CrashedProcess::Thread* thread,
253                     const MinidumpMemoryRange& range) {
254  const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0);
255
256  thread->regs.ebx = rawregs->ebx;
257  thread->regs.ecx = rawregs->ecx;
258  thread->regs.edx = rawregs->edx;
259  thread->regs.esi = rawregs->esi;
260  thread->regs.edi = rawregs->edi;
261  thread->regs.ebp = rawregs->ebp;
262  thread->regs.eax = rawregs->eax;
263  thread->regs.xds = rawregs->ds;
264  thread->regs.xes = rawregs->es;
265  thread->regs.xfs = rawregs->fs;
266  thread->regs.xgs = rawregs->gs;
267  thread->regs.orig_eax = rawregs->eax;
268  thread->regs.eip = rawregs->eip;
269  thread->regs.xcs = rawregs->cs;
270  thread->regs.eflags = rawregs->eflags;
271  thread->regs.esp = rawregs->esp;
272  thread->regs.xss = rawregs->ss;
273
274  thread->fpregs.cwd = rawregs->float_save.control_word;
275  thread->fpregs.swd = rawregs->float_save.status_word;
276  thread->fpregs.twd = rawregs->float_save.tag_word;
277  thread->fpregs.fip = rawregs->float_save.error_offset;
278  thread->fpregs.fcs = rawregs->float_save.error_selector;
279  thread->fpregs.foo = rawregs->float_save.data_offset;
280  thread->fpregs.fos = rawregs->float_save.data_selector;
281  memcpy(thread->fpregs.st_space, rawregs->float_save.register_area,
282         10 * 8);
283
284  thread->fpxregs.cwd = rawregs->float_save.control_word;
285  thread->fpxregs.swd = rawregs->float_save.status_word;
286  thread->fpxregs.twd = rawregs->float_save.tag_word;
287  thread->fpxregs.fop = U16(rawregs->extended_registers + 6);
288  thread->fpxregs.fip = U16(rawregs->extended_registers + 8);
289  thread->fpxregs.fcs = U16(rawregs->extended_registers + 12);
290  thread->fpxregs.foo = U16(rawregs->extended_registers + 16);
291  thread->fpxregs.fos = U16(rawregs->extended_registers + 20);
292  thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24);
293  memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128);
294  memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128);
295}
296#elif defined(__x86_64__)
297static void
298ParseThreadRegisters(CrashedProcess::Thread* thread,
299                     const MinidumpMemoryRange& range) {
300  const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0);
301
302  thread->regs.r15 = rawregs->r15;
303  thread->regs.r14 = rawregs->r14;
304  thread->regs.r13 = rawregs->r13;
305  thread->regs.r12 = rawregs->r12;
306  thread->regs.rbp = rawregs->rbp;
307  thread->regs.rbx = rawregs->rbx;
308  thread->regs.r11 = rawregs->r11;
309  thread->regs.r10 = rawregs->r10;
310  thread->regs.r9 = rawregs->r9;
311  thread->regs.r8 = rawregs->r8;
312  thread->regs.rax = rawregs->rax;
313  thread->regs.rcx = rawregs->rcx;
314  thread->regs.rdx = rawregs->rdx;
315  thread->regs.rsi = rawregs->rsi;
316  thread->regs.rdi = rawregs->rdi;
317  thread->regs.orig_rax = rawregs->rax;
318  thread->regs.rip = rawregs->rip;
319  thread->regs.cs  = rawregs->cs;
320  thread->regs.eflags = rawregs->eflags;
321  thread->regs.rsp = rawregs->rsp;
322  thread->regs.ss = rawregs->ss;
323  thread->regs.fs_base = 0;
324  thread->regs.gs_base = 0;
325  thread->regs.ds = rawregs->ds;
326  thread->regs.es = rawregs->es;
327  thread->regs.fs = rawregs->fs;
328  thread->regs.gs = rawregs->gs;
329
330  thread->fpregs.cwd = rawregs->flt_save.control_word;
331  thread->fpregs.swd = rawregs->flt_save.status_word;
332  thread->fpregs.ftw = rawregs->flt_save.tag_word;
333  thread->fpregs.fop = rawregs->flt_save.error_opcode;
334  thread->fpregs.rip = rawregs->flt_save.error_offset;
335  thread->fpregs.rdp = rawregs->flt_save.data_offset;
336  thread->fpregs.mxcsr = rawregs->flt_save.mx_csr;
337  thread->fpregs.mxcr_mask = rawregs->flt_save.mx_csr_mask;
338  memcpy(thread->fpregs.st_space, rawregs->flt_save.float_registers, 8 * 16);
339  memcpy(thread->fpregs.xmm_space, rawregs->flt_save.xmm_registers, 16 * 16);
340}
341#elif defined(__arm__)
342static void
343ParseThreadRegisters(CrashedProcess::Thread* thread,
344                     const MinidumpMemoryRange& range) {
345  const MDRawContextARM* rawregs = range.GetData<MDRawContextARM>(0);
346
347  thread->regs.uregs[0] = rawregs->iregs[0];
348  thread->regs.uregs[1] = rawregs->iregs[1];
349  thread->regs.uregs[2] = rawregs->iregs[2];
350  thread->regs.uregs[3] = rawregs->iregs[3];
351  thread->regs.uregs[4] = rawregs->iregs[4];
352  thread->regs.uregs[5] = rawregs->iregs[5];
353  thread->regs.uregs[6] = rawregs->iregs[6];
354  thread->regs.uregs[7] = rawregs->iregs[7];
355  thread->regs.uregs[8] = rawregs->iregs[8];
356  thread->regs.uregs[9] = rawregs->iregs[9];
357  thread->regs.uregs[10] = rawregs->iregs[10];
358  thread->regs.uregs[11] = rawregs->iregs[11];
359  thread->regs.uregs[12] = rawregs->iregs[12];
360  thread->regs.uregs[13] = rawregs->iregs[13];
361  thread->regs.uregs[14] = rawregs->iregs[14];
362  thread->regs.uregs[15] = rawregs->iregs[15];
363
364  thread->regs.uregs[16] = rawregs->cpsr;
365  thread->regs.uregs[17] = 0;  // what is ORIG_r0 exactly?
366}
367#elif defined(__mips__)
368static void
369ParseThreadRegisters(CrashedProcess::Thread* thread,
370                     const MinidumpMemoryRange& range) {
371  const MDRawContextMIPS* rawregs = range.GetData<MDRawContextMIPS>(0);
372
373  for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
374    thread->regs.regs[i] = rawregs->iregs[i];
375
376  thread->regs.lo = rawregs->mdlo;
377  thread->regs.hi = rawregs->mdhi;
378  thread->regs.epc = rawregs->epc;
379  thread->regs.badvaddr = rawregs->badvaddr;
380  thread->regs.status = rawregs->status;
381  thread->regs.cause = rawregs->cause;
382
383  for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
384    thread->fpregs.regs[i] = rawregs->float_save.regs[i];
385
386  thread->fpregs.fpcsr = rawregs->float_save.fpcsr;
387  thread->fpregs.fir = rawregs->float_save.fir;
388}
389#else
390#error "This code has not been ported to your platform yet"
391#endif
392
393static void
394ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
395                const MinidumpMemoryRange& full_file) {
396  const uint32_t num_threads = *range.GetData<uint32_t>(0);
397  if (verbose) {
398    fprintf(stderr,
399            "MD_THREAD_LIST_STREAM:\n"
400            "Found %d threads\n"
401            "\n\n",
402            num_threads);
403  }
404  for (unsigned i = 0; i < num_threads; ++i) {
405    CrashedProcess::Thread thread;
406    memset(&thread, 0, sizeof(thread));
407    const MDRawThread* rawthread =
408        range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i);
409    thread.tid = rawthread->thread_id;
410    thread.stack_addr = rawthread->stack.start_of_memory_range;
411    MinidumpMemoryRange stack_range =
412        full_file.Subrange(rawthread->stack.memory);
413    thread.stack = stack_range.data();
414    thread.stack_length = rawthread->stack.memory.data_size;
415
416    ParseThreadRegisters(&thread,
417                         full_file.Subrange(rawthread->thread_context));
418
419    crashinfo->threads.push_back(thread);
420  }
421}
422
423static void
424ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
425                const MinidumpMemoryRange& full_file) {
426  const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
427  if (!sysinfo) {
428    fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
429    _exit(1);
430  }
431#if defined(__i386__)
432  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
433    fprintf(stderr,
434            "This version of minidump-2-core only supports x86 (32bit)%s.\n",
435            sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ?
436            ",\nbut the minidump file is from a 64bit machine" : "");
437    _exit(1);
438  }
439#elif defined(__x86_64__)
440  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
441    fprintf(stderr,
442            "This version of minidump-2-core only supports x86 (64bit)%s.\n",
443            sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ?
444            ",\nbut the minidump file is from a 32bit machine" : "");
445    _exit(1);
446  }
447#elif defined(__arm__)
448  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
449    fprintf(stderr,
450            "This version of minidump-2-core only supports ARM (32bit).\n");
451    _exit(1);
452  }
453#elif defined(__mips__)
454  if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
455    fprintf(stderr,
456            "This version of minidump-2-core only supports mips (32bit).\n");
457    _exit(1);
458  }
459#else
460#error "This code has not been ported to your platform yet"
461#endif
462  if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
463              "Linux") &&
464      sysinfo->platform_id != MD_OS_NACL) {
465    fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n");
466    _exit(1);
467  }
468
469  if (verbose) {
470    fprintf(stderr,
471            "MD_SYSTEM_INFO_STREAM:\n"
472            "Architecture: %s\n"
473            "Number of processors: %d\n"
474            "Processor level: %d\n"
475            "Processor model: %d\n"
476            "Processor stepping: %d\n",
477            sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86
478            ? "i386"
479            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
480            ? "x86-64"
481            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
482            ? "ARM"
483            : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
484            ? "MIPS"
485            : "???",
486            sysinfo->number_of_processors,
487            sysinfo->processor_level,
488            sysinfo->processor_revision >> 8,
489            sysinfo->processor_revision & 0xFF);
490    if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
491        sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) {
492      fputs("Vendor id: ", stderr);
493      const char *nul =
494        (const char *)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0,
495                             sizeof(sysinfo->cpu.x86_cpu_info.vendor_id));
496      fwrite(sysinfo->cpu.x86_cpu_info.vendor_id,
497             nul ? nul - (const char *)&sysinfo->cpu.x86_cpu_info.vendor_id[0]
498             : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr);
499      fputs("\n", stderr);
500    }
501    fprintf(stderr, "OS: %s\n",
502            full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
503    fputs("\n\n", stderr);
504  }
505}
506
507static void
508ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
509  if (verbose) {
510    fputs("MD_LINUX_CPU_INFO:\n", stderr);
511    fwrite(range.data(), range.length(), 1, stderr);
512    fputs("\n\n\n", stderr);
513  }
514}
515
516static void
517ParseProcessStatus(CrashedProcess* crashinfo,
518                   const MinidumpMemoryRange& range) {
519  if (verbose) {
520    fputs("MD_LINUX_PROC_STATUS:\n", stderr);
521    fwrite(range.data(), range.length(), 1, stderr);
522    fputs("\n\n", stderr);
523  }
524}
525
526static void
527ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
528  if (verbose) {
529    fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
530    fwrite(range.data(), range.length(), 1, stderr);
531    fputs("\n\n", stderr);
532  }
533}
534
535static void
536ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
537  if (verbose) {
538    fputs("MD_LINUX_MAPS:\n", stderr);
539    fwrite(range.data(), range.length(), 1, stderr);
540  }
541  for (const uint8_t* ptr = range.data();
542       ptr < range.data() + range.length();) {
543    const uint8_t* eol = (uint8_t*)memchr(ptr, '\n',
544                                       range.data() + range.length() - ptr);
545    std::string line((const char*)ptr,
546                     eol ? eol - ptr : range.data() + range.length() - ptr);
547    ptr = eol ? eol + 1 : range.data() + range.length();
548    unsigned long long start, stop, offset;
549    char* permissions = NULL;
550    char* filename = NULL;
551    sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
552           &start, &stop, &permissions, &offset, &filename);
553    if (filename && *filename == '/') {
554      CrashedProcess::Mapping mapping;
555      mapping.permissions = 0;
556      if (strchr(permissions, 'r')) {
557        mapping.permissions |= PF_R;
558      }
559      if (strchr(permissions, 'w')) {
560        mapping.permissions |= PF_W;
561      }
562      if (strchr(permissions, 'x')) {
563        mapping.permissions |= PF_X;
564      }
565      mapping.start_address = start;
566      mapping.end_address = stop;
567      mapping.offset = offset;
568      if (filename) {
569        mapping.filename = filename;
570      }
571      crashinfo->mappings[mapping.start_address] = mapping;
572    }
573    free(permissions);
574    free(filename);
575  }
576  if (verbose) {
577    fputs("\n\n\n", stderr);
578  }
579}
580
581static void
582ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
583  if (verbose) {
584    fputs("MD_LINUX_ENVIRON:\n", stderr);
585    char* env = new char[range.length()];
586    memcpy(env, range.data(), range.length());
587    int nul_count = 0;
588    for (char *ptr = env;;) {
589      ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env));
590      if (!ptr) {
591        break;
592      }
593      if (ptr > env && ptr[-1] == '\n') {
594        if (++nul_count > 5) {
595          // Some versions of Chrome try to rewrite the process' command line
596          // in a way that causes the environment to be corrupted. Afterwards,
597          // part of the environment will contain the trailing bit of the
598          // command line. The rest of the environment will be filled with
599          // NUL bytes.
600          // We detect this corruption by counting the number of consecutive
601          // NUL bytes. Normally, we would not expect any consecutive NUL
602          // bytes. But we are conservative and only suppress printing of
603          // the environment if we see at least five consecutive NULs.
604          fputs("Environment has been corrupted; no data available", stderr);
605          goto env_corrupted;
606        }
607      } else {
608        nul_count = 0;
609      }
610      *ptr = '\n';
611    }
612    fwrite(env, range.length(), 1, stderr);
613  env_corrupted:
614    delete[] env;
615    fputs("\n\n\n", stderr);
616  }
617}
618
619static void
620ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
621  // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
622  // when dumping /proc/$x/maps
623  if (range.length() > 17) {
624    // The AUXV vector contains binary data, whereas the maps always begin
625    // with an 8+ digit hex address followed by a hyphen and another 8+ digit
626    // address.
627    char addresses[18];
628    memcpy(addresses, range.data(), 17);
629    addresses[17] = '\000';
630    if (strspn(addresses, "0123456789abcdef-") == 17) {
631      ParseMaps(crashinfo, range);
632      return;
633    }
634  }
635
636  crashinfo->auxv = range.data();
637  crashinfo->auxv_length = range.length();
638}
639
640static void
641ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
642  // The command line is supposed to use NUL bytes to separate arguments.
643  // As Chrome rewrites its own command line and (incorrectly) substitutes
644  // spaces, this is often not the case in our minidump files.
645  const char* cmdline = (const char*) range.data();
646  if (verbose) {
647    fputs("MD_LINUX_CMD_LINE:\n", stderr);
648    unsigned i = 0;
649    for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { }
650    fputs("argv[0] = \"", stderr);
651    fwrite(cmdline, i, 1, stderr);
652    fputs("\"\n", stderr);
653    for (unsigned j = ++i, argc = 1; j < range.length(); ++j) {
654      if (!cmdline[j] || cmdline[j] == ' ') {
655        fprintf(stderr, "argv[%d] = \"", argc++);
656        fwrite(cmdline + i, j - i, 1, stderr);
657        fputs("\"\n", stderr);
658        i = j + 1;
659      }
660    }
661    fputs("\n\n", stderr);
662  }
663
664  const char *binary_name = cmdline;
665  for (size_t i = 0; i < range.length(); ++i) {
666    if (cmdline[i] == '/') {
667      binary_name = cmdline + i + 1;
668    } else if (cmdline[i] == 0 || cmdline[i] == ' ') {
669      static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1;
670      static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1;
671      memset(crashinfo->prps.pr_fname, 0, fname_len + 1);
672      memset(crashinfo->prps.pr_psargs, 0, args_len + 1);
673      unsigned len = cmdline + i - binary_name;
674      memcpy(crashinfo->prps.pr_fname, binary_name,
675               len > fname_len ? fname_len : len);
676
677      len = range.length() > args_len ? args_len : range.length();
678      memcpy(crashinfo->prps.pr_psargs, cmdline, len);
679      for (unsigned j = 0; j < len; ++j) {
680        if (crashinfo->prps.pr_psargs[j] == 0)
681          crashinfo->prps.pr_psargs[j] = ' ';
682      }
683      break;
684    }
685  }
686}
687
688static void
689ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
690                  const MinidumpMemoryRange& full_file) {
691  const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
692  if (!debug) {
693    return;
694  }
695  if (verbose) {
696    fprintf(stderr,
697            "MD_LINUX_DSO_DEBUG:\n"
698            "Version: %d\n"
699            "Number of DSOs: %d\n"
700            "Brk handler: 0x%" PRIx64 "\n"
701            "Dynamic loader at: 0x%" PRIx64 "\n"
702            "_DYNAMIC: 0x%" PRIx64 "\n",
703            debug->version,
704            debug->dso_count,
705            static_cast<uint64_t>(debug->brk),
706            static_cast<uint64_t>(debug->ldbase),
707            static_cast<uint64_t>(debug->dynamic));
708  }
709  crashinfo->debug = *debug;
710  if (range.length() > sizeof(MDRawDebug)) {
711    char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug);
712    crashinfo->dynamic_data.assign(dynamic_data,
713                                   range.length() - sizeof(MDRawDebug));
714  }
715  if (debug->map != kInvalidMDRVA) {
716    for (unsigned int i = 0; i < debug->dso_count; ++i) {
717      const MDRawLinkMap* link_map =
718          full_file.GetArrayElement<MDRawLinkMap>(debug->map, i);
719      if (link_map) {
720        if (verbose) {
721          fprintf(stderr,
722                  "#%03d: %" PRIx64 ", %" PRIx64 ", \"%s\"\n",
723                  i, static_cast<uint64_t>(link_map->addr),
724                  static_cast<uint64_t>(link_map->ld),
725                  full_file.GetAsciiMDString(link_map->name).c_str());
726        }
727        crashinfo->link_map.push_back(*link_map);
728      }
729    }
730  }
731  if (verbose) {
732    fputs("\n\n", stderr);
733  }
734}
735
736static void
737ParseExceptionStream(CrashedProcess* crashinfo,
738                     const MinidumpMemoryRange& range) {
739  const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0);
740  crashinfo->crashing_tid = exp->thread_id;
741  crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
742}
743
744static bool
745WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
746  struct prstatus pr;
747  memset(&pr, 0, sizeof(pr));
748
749  pr.pr_info.si_signo = fatal_signal;
750  pr.pr_cursig = fatal_signal;
751  pr.pr_pid = thread.tid;
752  memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
753
754  Nhdr nhdr;
755  memset(&nhdr, 0, sizeof(nhdr));
756  nhdr.n_namesz = 5;
757  nhdr.n_descsz = sizeof(struct prstatus);
758  nhdr.n_type = NT_PRSTATUS;
759  if (!writea(1, &nhdr, sizeof(nhdr)) ||
760      !writea(1, "CORE\0\0\0\0", 8) ||
761      !writea(1, &pr, sizeof(struct prstatus))) {
762    return false;
763  }
764
765#if defined(__i386__) || defined(__x86_64__)
766  nhdr.n_descsz = sizeof(user_fpregs_struct);
767  nhdr.n_type = NT_FPREGSET;
768  if (!writea(1, &nhdr, sizeof(nhdr)) ||
769      !writea(1, "CORE\0\0\0\0", 8) ||
770      !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) {
771    return false;
772  }
773#endif
774
775#if defined(__i386__)
776  nhdr.n_descsz = sizeof(user_fpxregs_struct);
777  nhdr.n_type = NT_PRXFPREG;
778  if (!writea(1, &nhdr, sizeof(nhdr)) ||
779      !writea(1, "LINUX\0\0\0", 8) ||
780      !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
781    return false;
782  }
783#endif
784
785  return true;
786}
787
788static void
789ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
790                  const MinidumpMemoryRange& full_file) {
791  if (verbose) {
792    fputs("MD_MODULE_LIST_STREAM:\n", stderr);
793  }
794  const uint32_t num_mappings = *range.GetData<uint32_t>(0);
795  for (unsigned i = 0; i < num_mappings; ++i) {
796    CrashedProcess::Mapping mapping;
797    const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>(
798        range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i));
799    mapping.start_address = rawmodule->base_of_image;
800    mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
801
802    if (crashinfo->mappings.find(mapping.start_address) ==
803        crashinfo->mappings.end()) {
804      // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as
805      // the former is a strict superset of the latter.
806      crashinfo->mappings[mapping.start_address] = mapping;
807    }
808
809    const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
810        full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
811    char guid[40];
812    sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
813            record->signature.data1, record->signature.data2,
814            record->signature.data3,
815            record->signature.data4[0], record->signature.data4[1],
816            record->signature.data4[2], record->signature.data4[3],
817            record->signature.data4[4], record->signature.data4[5],
818            record->signature.data4[6], record->signature.data4[7]);
819    std::string filename =
820        full_file.GetAsciiMDString(rawmodule->module_name_rva);
821    size_t slash = filename.find_last_of('/');
822    std::string basename = slash == std::string::npos ?
823      filename : filename.substr(slash + 1);
824    if (strcmp(guid, "00000000-0000-0000-0000-000000000000")) {
825      std::string prefix;
826      if (!g_custom_so_basedir.empty())
827        prefix = g_custom_so_basedir;
828      else
829        prefix = std::string("/var/lib/breakpad/") + guid + "-" + basename;
830
831      crashinfo->signatures[rawmodule->base_of_image] = prefix + basename;
832    }
833
834    if (verbose) {
835      fprintf(stderr, "0x%08llX-0x%08llX, ChkSum: 0x%08X, GUID: %s, \"%s\"\n",
836              (unsigned long long)rawmodule->base_of_image,
837              (unsigned long long)rawmodule->base_of_image +
838              rawmodule->size_of_image,
839              rawmodule->checksum, guid, filename.c_str());
840    }
841  }
842  if (verbose) {
843    fputs("\n\n", stderr);
844  }
845}
846
847static void
848AddDataToMapping(CrashedProcess* crashinfo, const std::string& data,
849                 uintptr_t addr) {
850  for (std::map<uint64_t, CrashedProcess::Mapping>::iterator
851         iter = crashinfo->mappings.begin();
852       iter != crashinfo->mappings.end();
853       ++iter) {
854    if (addr >= iter->second.start_address &&
855        addr < iter->second.end_address) {
856      CrashedProcess::Mapping mapping = iter->second;
857      if ((addr & ~4095) != iter->second.start_address) {
858        // If there are memory pages in the mapping prior to where the
859        // data starts, truncate the existing mapping so that it ends with
860        // the page immediately preceding the data region.
861        iter->second.end_address = addr & ~4095;
862        if (!mapping.filename.empty()) {
863          // "mapping" is a copy of "iter->second". We are splitting the
864          // existing mapping into two separate ones when we write the data
865          // to the core file. The first one does not have any associated
866          // data in the core file, the second one is backed by data that is
867          // included with the core file.
868          // If this mapping wasn't supposed to be anonymous, then we also
869          // have to update the file offset upon splitting the mapping.
870          mapping.offset += iter->second.end_address -
871            iter->second.start_address;
872        }
873      }
874      // Create a new mapping that contains the data contents. We often
875      // limit the amount of data that is actually written to the core
876      // file. But it is OK if the mapping itself extends past the end of
877      // the data.
878      mapping.start_address = addr & ~4095;
879      mapping.data.assign(addr & 4095, 0).append(data);
880      mapping.data.append(-mapping.data.size() & 4095, 0);
881      crashinfo->mappings[mapping.start_address] = mapping;
882      return;
883    }
884  }
885  // Didn't find a suitable existing mapping for the data. Create a new one.
886  CrashedProcess::Mapping mapping;
887  mapping.permissions = PF_R | PF_W;
888  mapping.start_address = addr & ~4095;
889  mapping.end_address =
890    (addr + data.size() + 4095) & ~4095;
891  mapping.data.assign(addr & 4095, 0).append(data);
892  mapping.data.append(-mapping.data.size() & 4095, 0);
893  crashinfo->mappings[mapping.start_address] = mapping;
894}
895
896static void
897AugmentMappings(CrashedProcess* crashinfo,
898                const MinidumpMemoryRange& full_file) {
899  // For each thread, find the memory mapping that matches the thread's stack.
900  // Then adjust the mapping to include the stack dump.
901  for (unsigned i = 0; i < crashinfo->threads.size(); ++i) {
902    const CrashedProcess::Thread& thread = crashinfo->threads[i];
903    AddDataToMapping(crashinfo,
904                     std::string((char *)thread.stack, thread.stack_length),
905                     thread.stack_addr);
906  }
907
908  // Create a new link map with information about DSOs. We move this map to
909  // the beginning of the address space, as this area should always be
910  // available.
911  static const uintptr_t start_addr = 4096;
912  std::string data;
913  struct r_debug debug = { 0 };
914  debug.r_version = crashinfo->debug.version;
915  debug.r_brk = (ElfW(Addr))crashinfo->debug.brk;
916  debug.r_state = r_debug::RT_CONSISTENT;
917  debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase;
918  debug.r_map = crashinfo->debug.dso_count > 0 ?
919    (struct link_map*)(start_addr + sizeof(debug)) : 0;
920  data.append((char*)&debug, sizeof(debug));
921
922  struct link_map* prev = 0;
923  for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin();
924       iter != crashinfo->link_map.end();
925       ++iter) {
926    struct link_map link_map = { 0 };
927    link_map.l_addr = (ElfW(Addr))iter->addr;
928    link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map));
929    link_map.l_ld = (ElfW(Dyn)*)iter->ld;
930    link_map.l_prev = prev;
931    prev = (struct link_map*)(start_addr + data.size());
932    std::string filename = full_file.GetAsciiMDString(iter->name);
933
934    // Look up signature for this filename. If available, change filename
935    // to point to GUID, instead.
936    std::map<uintptr_t, std::string>::const_iterator guid =
937      crashinfo->signatures.find((uintptr_t)iter->addr);
938    if (guid != crashinfo->signatures.end()) {
939      filename = guid->second;
940    }
941
942    if (std::distance(iter, crashinfo->link_map.end()) == 1) {
943      link_map.l_next = 0;
944    } else {
945      link_map.l_next = (struct link_map*)(start_addr + data.size() +
946                                           sizeof(link_map) +
947                                           ((filename.size() + 8) & ~7));
948    }
949    data.append((char*)&link_map, sizeof(link_map));
950    data.append(filename);
951    data.append(8 - (filename.size() & 7), 0);
952  }
953  AddDataToMapping(crashinfo, data, start_addr);
954
955  // Map the page containing the _DYNAMIC array
956  if (!crashinfo->dynamic_data.empty()) {
957    // Make _DYNAMIC DT_DEBUG entry point to our link map
958    for (int i = 0;; ++i) {
959      ElfW(Dyn) dyn;
960      if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) {
961      no_dt_debug:
962        if (verbose) {
963          fprintf(stderr, "No DT_DEBUG entry found\n");
964        }
965        return;
966      }
967      memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn),
968             sizeof(dyn));
969      if (dyn.d_tag == DT_DEBUG) {
970        crashinfo->dynamic_data.replace(i*sizeof(dyn) +
971                                       offsetof(ElfW(Dyn), d_un.d_ptr),
972                                       sizeof(start_addr),
973                                       (char*)&start_addr, sizeof(start_addr));
974        break;
975      } else if (dyn.d_tag == DT_NULL) {
976        goto no_dt_debug;
977      }
978    }
979    AddDataToMapping(crashinfo, crashinfo->dynamic_data,
980                     (uintptr_t)crashinfo->debug.dynamic);
981  }
982}
983
984int
985main(int argc, char** argv) {
986  int argi = 1;
987  while (argi < argc && argv[argi][0] == '-') {
988    if (!strcmp(argv[argi], "-v")) {
989      verbose = true;
990    } else if (!strcmp(argv[argi], "--sobasedir")) {
991      argi++;
992      if (argi >= argc) {
993        fprintf(stderr, "--sobasedir expects an argument.");
994        return usage(argv[0]);
995      }
996
997      g_custom_so_basedir = argv[argi];
998    } else {
999      return usage(argv[0]);
1000    }
1001    argi++;
1002  }
1003
1004  if (argc != argi + 1)
1005    return usage(argv[0]);
1006
1007  MemoryMappedFile mapped_file(argv[argi], 0);
1008  if (!mapped_file.data()) {
1009    fprintf(stderr, "Failed to mmap dump file\n");
1010    return 1;
1011  }
1012
1013  MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
1014
1015  const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
1016
1017  CrashedProcess crashinfo;
1018
1019  // Always check the system info first, as that allows us to tell whether
1020  // this is a minidump file that is compatible with our converter.
1021  bool ok = false;
1022  for (unsigned i = 0; i < header->stream_count; ++i) {
1023    const MDRawDirectory* dirent =
1024        dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1025    switch (dirent->stream_type) {
1026      case MD_SYSTEM_INFO_STREAM:
1027        ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1028        ok = true;
1029        break;
1030      default:
1031        break;
1032    }
1033  }
1034  if (!ok) {
1035    fprintf(stderr, "Cannot determine input file format.\n");
1036    _exit(1);
1037  }
1038
1039  for (unsigned i = 0; i < header->stream_count; ++i) {
1040    const MDRawDirectory* dirent =
1041        dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1042    switch (dirent->stream_type) {
1043      case MD_THREAD_LIST_STREAM:
1044        ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
1045        break;
1046      case MD_LINUX_CPU_INFO:
1047        ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location));
1048        break;
1049      case MD_LINUX_PROC_STATUS:
1050        ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location));
1051        break;
1052      case MD_LINUX_LSB_RELEASE:
1053        ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location));
1054        break;
1055      case MD_LINUX_ENVIRON:
1056        ParseEnvironment(&crashinfo, dump.Subrange(dirent->location));
1057        break;
1058      case MD_LINUX_MAPS:
1059        ParseMaps(&crashinfo, dump.Subrange(dirent->location));
1060        break;
1061      case MD_LINUX_AUXV:
1062        ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
1063        break;
1064      case MD_LINUX_CMD_LINE:
1065        ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
1066        break;
1067      case MD_LINUX_DSO_DEBUG:
1068        ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1069        break;
1070      case MD_EXCEPTION_STREAM:
1071        ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
1072        break;
1073      case MD_MODULE_LIST_STREAM:
1074        ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump);
1075        break;
1076      default:
1077        if (verbose)
1078          fprintf(stderr, "Skipping %x\n", dirent->stream_type);
1079    }
1080  }
1081
1082  AugmentMappings(&crashinfo, dump);
1083
1084  // Write the ELF header. The file will look like:
1085  //   ELF header
1086  //   Phdr for the PT_NOTE
1087  //   Phdr for each of the thread stacks
1088  //   PT_NOTE
1089  //   each of the thread stacks
1090  Ehdr ehdr;
1091  memset(&ehdr, 0, sizeof(Ehdr));
1092  ehdr.e_ident[0] = ELFMAG0;
1093  ehdr.e_ident[1] = ELFMAG1;
1094  ehdr.e_ident[2] = ELFMAG2;
1095  ehdr.e_ident[3] = ELFMAG3;
1096  ehdr.e_ident[4] = ELF_CLASS;
1097  ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
1098  ehdr.e_ident[6] = EV_CURRENT;
1099  ehdr.e_type     = ET_CORE;
1100  ehdr.e_machine  = ELF_ARCH;
1101  ehdr.e_version  = EV_CURRENT;
1102  ehdr.e_phoff    = sizeof(Ehdr);
1103  ehdr.e_ehsize   = sizeof(Ehdr);
1104  ehdr.e_phentsize= sizeof(Phdr);
1105  ehdr.e_phnum    = 1 +                         // PT_NOTE
1106                    crashinfo.mappings.size();  // memory mappings
1107  ehdr.e_shentsize= sizeof(Shdr);
1108  if (!writea(1, &ehdr, sizeof(Ehdr)))
1109    return 1;
1110
1111  size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr);
1112  size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
1113                  // sizeof(Nhdr) + 8 + sizeof(user) +
1114                  sizeof(Nhdr) + 8 + crashinfo.auxv_length +
1115                  crashinfo.threads.size() * (
1116                    (sizeof(Nhdr) + 8 + sizeof(prstatus))
1117#if defined(__i386__) || defined(__x86_64__)
1118                   + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct)
1119#endif
1120#if defined(__i386__)
1121                   + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
1122#endif
1123                    );
1124
1125  Phdr phdr;
1126  memset(&phdr, 0, sizeof(Phdr));
1127  phdr.p_type = PT_NOTE;
1128  phdr.p_offset = offset;
1129  phdr.p_filesz = filesz;
1130  if (!writea(1, &phdr, sizeof(phdr)))
1131    return 1;
1132
1133  phdr.p_type = PT_LOAD;
1134  phdr.p_align = 4096;
1135  size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
1136  if (note_align == phdr.p_align)
1137    note_align = 0;
1138  offset += note_align;
1139
1140  for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1141         crashinfo.mappings.begin();
1142       iter != crashinfo.mappings.end(); ++iter) {
1143    const CrashedProcess::Mapping& mapping = iter->second;
1144    if (mapping.permissions == 0xFFFFFFFF) {
1145      // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to
1146      // MD_LINUX_MAPS). It lacks some of the information that we would like
1147      // to include.
1148      phdr.p_flags = PF_R;
1149    } else {
1150      phdr.p_flags = mapping.permissions;
1151    }
1152    phdr.p_vaddr = mapping.start_address;
1153    phdr.p_memsz = mapping.end_address - mapping.start_address;
1154    if (mapping.data.size()) {
1155      offset += filesz;
1156      filesz = mapping.data.size();
1157      phdr.p_filesz = mapping.data.size();
1158      phdr.p_offset = offset;
1159    } else {
1160      phdr.p_filesz = 0;
1161      phdr.p_offset = 0;
1162    }
1163    if (!writea(1, &phdr, sizeof(phdr)))
1164      return 1;
1165  }
1166
1167  Nhdr nhdr;
1168  memset(&nhdr, 0, sizeof(nhdr));
1169  nhdr.n_namesz = 5;
1170  nhdr.n_descsz = sizeof(prpsinfo);
1171  nhdr.n_type = NT_PRPSINFO;
1172  if (!writea(1, &nhdr, sizeof(nhdr)) ||
1173      !writea(1, "CORE\0\0\0\0", 8) ||
1174      !writea(1, &crashinfo.prps, sizeof(prpsinfo))) {
1175    return 1;
1176  }
1177
1178  nhdr.n_descsz = crashinfo.auxv_length;
1179  nhdr.n_type = NT_AUXV;
1180  if (!writea(1, &nhdr, sizeof(nhdr)) ||
1181      !writea(1, "CORE\0\0\0\0", 8) ||
1182      !writea(1, crashinfo.auxv, crashinfo.auxv_length)) {
1183    return 1;
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], crashinfo.fatal_signal);
1189      break;
1190    }
1191  }
1192
1193  for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1194    if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
1195      WriteThread(crashinfo.threads[i], 0);
1196  }
1197
1198  if (note_align) {
1199    google_breakpad::scoped_array<char> scratch(new char[note_align]);
1200    memset(scratch.get(), 0, note_align);
1201    if (!writea(1, scratch.get(), note_align))
1202      return 1;
1203  }
1204
1205  for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1206         crashinfo.mappings.begin();
1207       iter != crashinfo.mappings.end(); ++iter) {
1208    const CrashedProcess::Mapping& mapping = iter->second;
1209    if (mapping.data.size()) {
1210      if (!writea(1, mapping.data.c_str(), mapping.data.size()))
1211        return 1;
1212    }
1213  }
1214
1215  return 0;
1216}
1217