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.
83 typedef user_regs user_regs_struct;
84 #endif
85 
86 using google_breakpad::MDTypeHelper;
87 using google_breakpad::MemoryMappedFile;
88 using google_breakpad::MinidumpMemoryRange;
89 
90 typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawDebug MDRawDebug;
91 typedef MDTypeHelper<sizeof(ElfW(Addr))>::MDRawLinkMap MDRawLinkMap;
92 
93 static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
94 static bool verbose;
95 static std::string g_custom_so_basedir;
96 
usage(const char * argv0)97 static 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.
104 static bool
writea(int fd,const void * idata,size_t length)105 writea(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  */
sex()126 static inline int sex() {
127   int probe = 1;
128   return !*(char *)&probe;
129 }
130 
131 typedef struct elf_timeval {    /* Time value with microsecond resolution    */
132   long tv_sec;                  /* Seconds                                   */
133   long tv_usec;                 /* Microseconds                              */
134 } elf_timeval;
135 
136 typedef 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 
142 typedef 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 
159 typedef 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
181 struct CrashedProcess {
CrashedProcessCrashedProcess182   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 {
MappingCrashedProcess::Mapping192     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__)
237 static uint32_t
U32(const uint8_t * data)238 U32(const uint8_t* data) {
239   uint32_t v;
240   memcpy(&v, data, sizeof(v));
241   return v;
242 }
243 
244 static uint16_t
U16(const uint8_t * data)245 U16(const uint8_t* data) {
246   uint16_t v;
247   memcpy(&v, data, sizeof(v));
248   return v;
249 }
250 
251 static void
ParseThreadRegisters(CrashedProcess::Thread * thread,const MinidumpMemoryRange & range)252 ParseThreadRegisters(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__)
297 static void
ParseThreadRegisters(CrashedProcess::Thread * thread,const MinidumpMemoryRange & range)298 ParseThreadRegisters(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__)
342 static void
ParseThreadRegisters(CrashedProcess::Thread * thread,const MinidumpMemoryRange & range)343 ParseThreadRegisters(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__)
368 static void
ParseThreadRegisters(CrashedProcess::Thread * thread,const MinidumpMemoryRange & range)369 ParseThreadRegisters(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 
393 static void
ParseThreadList(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)394 ParseThreadList(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 
423 static void
ParseSystemInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)424 ParseSystemInfo(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 
507 static void
ParseCPUInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)508 ParseCPUInfo(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 
516 static void
ParseProcessStatus(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)517 ParseProcessStatus(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 
526 static void
ParseLSBRelease(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)527 ParseLSBRelease(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 
535 static void
ParseMaps(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)536 ParseMaps(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 
581 static void
ParseEnvironment(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)582 ParseEnvironment(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 
619 static void
ParseAuxVector(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)620 ParseAuxVector(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 
640 static void
ParseCmdLine(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)641 ParseCmdLine(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 
688 static void
ParseDSODebugInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)689 ParseDSODebugInfo(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 
736 static void
ParseExceptionStream(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)737 ParseExceptionStream(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 
744 static bool
WriteThread(const CrashedProcess::Thread & thread,int fatal_signal)745 WriteThread(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 
788 static void
ParseModuleStream(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)789 ParseModuleStream(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 
847 static void
AddDataToMapping(CrashedProcess * crashinfo,const std::string & data,uintptr_t addr)848 AddDataToMapping(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 
896 static void
AugmentMappings(CrashedProcess * crashinfo,const MinidumpMemoryRange & full_file)897 AugmentMappings(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 
984 int
main(int argc,char ** argv)985 main(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