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