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