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