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