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