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