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