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 thread->fpregs.fir = rawregs->float_save.fir;
388 }
389 #else
390 #error "This code has not been ported to your platform yet"
391 #endif
392
393 static void
ParseThreadList(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)394 ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
395 const MinidumpMemoryRange& full_file) {
396 const uint32_t num_threads = *range.GetData<uint32_t>(0);
397 if (verbose) {
398 fprintf(stderr,
399 "MD_THREAD_LIST_STREAM:\n"
400 "Found %d threads\n"
401 "\n\n",
402 num_threads);
403 }
404 for (unsigned i = 0; i < num_threads; ++i) {
405 CrashedProcess::Thread thread;
406 memset(&thread, 0, sizeof(thread));
407 const MDRawThread* rawthread =
408 range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i);
409 thread.tid = rawthread->thread_id;
410 thread.stack_addr = rawthread->stack.start_of_memory_range;
411 MinidumpMemoryRange stack_range =
412 full_file.Subrange(rawthread->stack.memory);
413 thread.stack = stack_range.data();
414 thread.stack_length = rawthread->stack.memory.data_size;
415
416 ParseThreadRegisters(&thread,
417 full_file.Subrange(rawthread->thread_context));
418
419 crashinfo->threads.push_back(thread);
420 }
421 }
422
423 static void
ParseSystemInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)424 ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
425 const MinidumpMemoryRange& full_file) {
426 const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
427 if (!sysinfo) {
428 fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
429 _exit(1);
430 }
431 #if defined(__i386__)
432 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_X86) {
433 fprintf(stderr,
434 "This version of minidump-2-core only supports x86 (32bit)%s.\n",
435 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64 ?
436 ",\nbut the minidump file is from a 64bit machine" : "");
437 _exit(1);
438 }
439 #elif defined(__x86_64__)
440 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_AMD64) {
441 fprintf(stderr,
442 "This version of minidump-2-core only supports x86 (64bit)%s.\n",
443 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ?
444 ",\nbut the minidump file is from a 32bit machine" : "");
445 _exit(1);
446 }
447 #elif defined(__arm__)
448 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_ARM) {
449 fprintf(stderr,
450 "This version of minidump-2-core only supports ARM (32bit).\n");
451 _exit(1);
452 }
453 #elif defined(__mips__)
454 if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_MIPS) {
455 fprintf(stderr,
456 "This version of minidump-2-core only supports mips (32bit).\n");
457 _exit(1);
458 }
459 #else
460 #error "This code has not been ported to your platform yet"
461 #endif
462 if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
463 "Linux") &&
464 sysinfo->platform_id != MD_OS_NACL) {
465 fprintf(stderr, "This minidump was not generated by Linux or NaCl.\n");
466 _exit(1);
467 }
468
469 if (verbose) {
470 fprintf(stderr,
471 "MD_SYSTEM_INFO_STREAM:\n"
472 "Architecture: %s\n"
473 "Number of processors: %d\n"
474 "Processor level: %d\n"
475 "Processor model: %d\n"
476 "Processor stepping: %d\n",
477 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86
478 ? "i386"
479 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
480 ? "x86-64"
481 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_ARM
482 ? "ARM"
483 : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS
484 ? "MIPS"
485 : "???",
486 sysinfo->number_of_processors,
487 sysinfo->processor_level,
488 sysinfo->processor_revision >> 8,
489 sysinfo->processor_revision & 0xFF);
490 if (sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_X86 ||
491 sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_AMD64) {
492 fputs("Vendor id: ", stderr);
493 const char *nul =
494 (const char *)memchr(sysinfo->cpu.x86_cpu_info.vendor_id, 0,
495 sizeof(sysinfo->cpu.x86_cpu_info.vendor_id));
496 fwrite(sysinfo->cpu.x86_cpu_info.vendor_id,
497 nul ? nul - (const char *)&sysinfo->cpu.x86_cpu_info.vendor_id[0]
498 : sizeof(sysinfo->cpu.x86_cpu_info.vendor_id), 1, stderr);
499 fputs("\n", stderr);
500 }
501 fprintf(stderr, "OS: %s\n",
502 full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
503 fputs("\n\n", stderr);
504 }
505 }
506
507 static void
ParseCPUInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)508 ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
509 if (verbose) {
510 fputs("MD_LINUX_CPU_INFO:\n", stderr);
511 fwrite(range.data(), range.length(), 1, stderr);
512 fputs("\n\n\n", stderr);
513 }
514 }
515
516 static void
ParseProcessStatus(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)517 ParseProcessStatus(CrashedProcess* crashinfo,
518 const MinidumpMemoryRange& range) {
519 if (verbose) {
520 fputs("MD_LINUX_PROC_STATUS:\n", stderr);
521 fwrite(range.data(), range.length(), 1, stderr);
522 fputs("\n\n", stderr);
523 }
524 }
525
526 static void
ParseLSBRelease(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)527 ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
528 if (verbose) {
529 fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
530 fwrite(range.data(), range.length(), 1, stderr);
531 fputs("\n\n", stderr);
532 }
533 }
534
535 static void
ParseMaps(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)536 ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
537 if (verbose) {
538 fputs("MD_LINUX_MAPS:\n", stderr);
539 fwrite(range.data(), range.length(), 1, stderr);
540 }
541 for (const uint8_t* ptr = range.data();
542 ptr < range.data() + range.length();) {
543 const uint8_t* eol = (uint8_t*)memchr(ptr, '\n',
544 range.data() + range.length() - ptr);
545 std::string line((const char*)ptr,
546 eol ? eol - ptr : range.data() + range.length() - ptr);
547 ptr = eol ? eol + 1 : range.data() + range.length();
548 unsigned long long start, stop, offset;
549 char* permissions = NULL;
550 char* filename = NULL;
551 sscanf(line.c_str(), "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
552 &start, &stop, &permissions, &offset, &filename);
553 if (filename && *filename == '/') {
554 CrashedProcess::Mapping mapping;
555 mapping.permissions = 0;
556 if (strchr(permissions, 'r')) {
557 mapping.permissions |= PF_R;
558 }
559 if (strchr(permissions, 'w')) {
560 mapping.permissions |= PF_W;
561 }
562 if (strchr(permissions, 'x')) {
563 mapping.permissions |= PF_X;
564 }
565 mapping.start_address = start;
566 mapping.end_address = stop;
567 mapping.offset = offset;
568 if (filename) {
569 mapping.filename = filename;
570 }
571 crashinfo->mappings[mapping.start_address] = mapping;
572 }
573 free(permissions);
574 free(filename);
575 }
576 if (verbose) {
577 fputs("\n\n\n", stderr);
578 }
579 }
580
581 static void
ParseEnvironment(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)582 ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
583 if (verbose) {
584 fputs("MD_LINUX_ENVIRON:\n", stderr);
585 char* env = new char[range.length()];
586 memcpy(env, range.data(), range.length());
587 int nul_count = 0;
588 for (char *ptr = env;;) {
589 ptr = (char *)memchr(ptr, '\000', range.length() - (ptr - env));
590 if (!ptr) {
591 break;
592 }
593 if (ptr > env && ptr[-1] == '\n') {
594 if (++nul_count > 5) {
595 // Some versions of Chrome try to rewrite the process' command line
596 // in a way that causes the environment to be corrupted. Afterwards,
597 // part of the environment will contain the trailing bit of the
598 // command line. The rest of the environment will be filled with
599 // NUL bytes.
600 // We detect this corruption by counting the number of consecutive
601 // NUL bytes. Normally, we would not expect any consecutive NUL
602 // bytes. But we are conservative and only suppress printing of
603 // the environment if we see at least five consecutive NULs.
604 fputs("Environment has been corrupted; no data available", stderr);
605 goto env_corrupted;
606 }
607 } else {
608 nul_count = 0;
609 }
610 *ptr = '\n';
611 }
612 fwrite(env, range.length(), 1, stderr);
613 env_corrupted:
614 delete[] env;
615 fputs("\n\n\n", stderr);
616 }
617 }
618
619 static void
ParseAuxVector(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)620 ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
621 // Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
622 // when dumping /proc/$x/maps
623 if (range.length() > 17) {
624 // The AUXV vector contains binary data, whereas the maps always begin
625 // with an 8+ digit hex address followed by a hyphen and another 8+ digit
626 // address.
627 char addresses[18];
628 memcpy(addresses, range.data(), 17);
629 addresses[17] = '\000';
630 if (strspn(addresses, "0123456789abcdef-") == 17) {
631 ParseMaps(crashinfo, range);
632 return;
633 }
634 }
635
636 crashinfo->auxv = range.data();
637 crashinfo->auxv_length = range.length();
638 }
639
640 static void
ParseCmdLine(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)641 ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
642 // The command line is supposed to use NUL bytes to separate arguments.
643 // As Chrome rewrites its own command line and (incorrectly) substitutes
644 // spaces, this is often not the case in our minidump files.
645 const char* cmdline = (const char*) range.data();
646 if (verbose) {
647 fputs("MD_LINUX_CMD_LINE:\n", stderr);
648 unsigned i = 0;
649 for (; i < range.length() && cmdline[i] && cmdline[i] != ' '; ++i) { }
650 fputs("argv[0] = \"", stderr);
651 fwrite(cmdline, i, 1, stderr);
652 fputs("\"\n", stderr);
653 for (unsigned j = ++i, argc = 1; j < range.length(); ++j) {
654 if (!cmdline[j] || cmdline[j] == ' ') {
655 fprintf(stderr, "argv[%d] = \"", argc++);
656 fwrite(cmdline + i, j - i, 1, stderr);
657 fputs("\"\n", stderr);
658 i = j + 1;
659 }
660 }
661 fputs("\n\n", stderr);
662 }
663
664 const char *binary_name = cmdline;
665 for (size_t i = 0; i < range.length(); ++i) {
666 if (cmdline[i] == '/') {
667 binary_name = cmdline + i + 1;
668 } else if (cmdline[i] == 0 || cmdline[i] == ' ') {
669 static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1;
670 static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1;
671 memset(crashinfo->prps.pr_fname, 0, fname_len + 1);
672 memset(crashinfo->prps.pr_psargs, 0, args_len + 1);
673 unsigned len = cmdline + i - binary_name;
674 memcpy(crashinfo->prps.pr_fname, binary_name,
675 len > fname_len ? fname_len : len);
676
677 len = range.length() > args_len ? args_len : range.length();
678 memcpy(crashinfo->prps.pr_psargs, cmdline, len);
679 for (unsigned j = 0; j < len; ++j) {
680 if (crashinfo->prps.pr_psargs[j] == 0)
681 crashinfo->prps.pr_psargs[j] = ' ';
682 }
683 break;
684 }
685 }
686 }
687
688 static void
ParseDSODebugInfo(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)689 ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
690 const MinidumpMemoryRange& full_file) {
691 const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
692 if (!debug) {
693 return;
694 }
695 if (verbose) {
696 fprintf(stderr,
697 "MD_LINUX_DSO_DEBUG:\n"
698 "Version: %d\n"
699 "Number of DSOs: %d\n"
700 "Brk handler: 0x%" PRIx64 "\n"
701 "Dynamic loader at: 0x%" PRIx64 "\n"
702 "_DYNAMIC: 0x%" PRIx64 "\n",
703 debug->version,
704 debug->dso_count,
705 static_cast<uint64_t>(debug->brk),
706 static_cast<uint64_t>(debug->ldbase),
707 static_cast<uint64_t>(debug->dynamic));
708 }
709 crashinfo->debug = *debug;
710 if (range.length() > sizeof(MDRawDebug)) {
711 char* dynamic_data = (char*)range.data() + sizeof(MDRawDebug);
712 crashinfo->dynamic_data.assign(dynamic_data,
713 range.length() - sizeof(MDRawDebug));
714 }
715 if (debug->map != kInvalidMDRVA) {
716 for (unsigned int i = 0; i < debug->dso_count; ++i) {
717 const MDRawLinkMap* link_map =
718 full_file.GetArrayElement<MDRawLinkMap>(debug->map, i);
719 if (link_map) {
720 if (verbose) {
721 fprintf(stderr,
722 "#%03d: %" PRIx64 ", %" PRIx64 ", \"%s\"\n",
723 i, static_cast<uint64_t>(link_map->addr),
724 static_cast<uint64_t>(link_map->ld),
725 full_file.GetAsciiMDString(link_map->name).c_str());
726 }
727 crashinfo->link_map.push_back(*link_map);
728 }
729 }
730 }
731 if (verbose) {
732 fputs("\n\n", stderr);
733 }
734 }
735
736 static void
ParseExceptionStream(CrashedProcess * crashinfo,const MinidumpMemoryRange & range)737 ParseExceptionStream(CrashedProcess* crashinfo,
738 const MinidumpMemoryRange& range) {
739 const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0);
740 crashinfo->crashing_tid = exp->thread_id;
741 crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
742 }
743
744 static bool
WriteThread(const CrashedProcess::Thread & thread,int fatal_signal)745 WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
746 struct prstatus pr;
747 memset(&pr, 0, sizeof(pr));
748
749 pr.pr_info.si_signo = fatal_signal;
750 pr.pr_cursig = fatal_signal;
751 pr.pr_pid = thread.tid;
752 memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
753
754 Nhdr nhdr;
755 memset(&nhdr, 0, sizeof(nhdr));
756 nhdr.n_namesz = 5;
757 nhdr.n_descsz = sizeof(struct prstatus);
758 nhdr.n_type = NT_PRSTATUS;
759 if (!writea(1, &nhdr, sizeof(nhdr)) ||
760 !writea(1, "CORE\0\0\0\0", 8) ||
761 !writea(1, &pr, sizeof(struct prstatus))) {
762 return false;
763 }
764
765 #if defined(__i386__) || defined(__x86_64__)
766 nhdr.n_descsz = sizeof(user_fpregs_struct);
767 nhdr.n_type = NT_FPREGSET;
768 if (!writea(1, &nhdr, sizeof(nhdr)) ||
769 !writea(1, "CORE\0\0\0\0", 8) ||
770 !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) {
771 return false;
772 }
773 #endif
774
775 #if defined(__i386__)
776 nhdr.n_descsz = sizeof(user_fpxregs_struct);
777 nhdr.n_type = NT_PRXFPREG;
778 if (!writea(1, &nhdr, sizeof(nhdr)) ||
779 !writea(1, "LINUX\0\0\0", 8) ||
780 !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
781 return false;
782 }
783 #endif
784
785 return true;
786 }
787
788 static void
ParseModuleStream(CrashedProcess * crashinfo,const MinidumpMemoryRange & range,const MinidumpMemoryRange & full_file)789 ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
790 const MinidumpMemoryRange& full_file) {
791 if (verbose) {
792 fputs("MD_MODULE_LIST_STREAM:\n", stderr);
793 }
794 const uint32_t num_mappings = *range.GetData<uint32_t>(0);
795 for (unsigned i = 0; i < num_mappings; ++i) {
796 CrashedProcess::Mapping mapping;
797 const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>(
798 range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i));
799 mapping.start_address = rawmodule->base_of_image;
800 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
801
802 if (crashinfo->mappings.find(mapping.start_address) ==
803 crashinfo->mappings.end()) {
804 // We prefer data from MD_LINUX_MAPS over MD_MODULE_LIST_STREAM, as
805 // the former is a strict superset of the latter.
806 crashinfo->mappings[mapping.start_address] = mapping;
807 }
808
809 const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
810 full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
811 char guid[40];
812 sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
813 record->signature.data1, record->signature.data2,
814 record->signature.data3,
815 record->signature.data4[0], record->signature.data4[1],
816 record->signature.data4[2], record->signature.data4[3],
817 record->signature.data4[4], record->signature.data4[5],
818 record->signature.data4[6], record->signature.data4[7]);
819 std::string filename =
820 full_file.GetAsciiMDString(rawmodule->module_name_rva);
821 size_t slash = filename.find_last_of('/');
822 std::string basename = slash == std::string::npos ?
823 filename : filename.substr(slash + 1);
824 if (strcmp(guid, "00000000-0000-0000-0000-000000000000")) {
825 std::string prefix;
826 if (!g_custom_so_basedir.empty())
827 prefix = g_custom_so_basedir;
828 else
829 prefix = std::string("/var/lib/breakpad/") + guid + "-" + basename;
830
831 crashinfo->signatures[rawmodule->base_of_image] = prefix + basename;
832 }
833
834 if (verbose) {
835 fprintf(stderr, "0x%08llX-0x%08llX, ChkSum: 0x%08X, GUID: %s, \"%s\"\n",
836 (unsigned long long)rawmodule->base_of_image,
837 (unsigned long long)rawmodule->base_of_image +
838 rawmodule->size_of_image,
839 rawmodule->checksum, guid, filename.c_str());
840 }
841 }
842 if (verbose) {
843 fputs("\n\n", stderr);
844 }
845 }
846
847 static void
AddDataToMapping(CrashedProcess * crashinfo,const std::string & data,uintptr_t addr)848 AddDataToMapping(CrashedProcess* crashinfo, const std::string& data,
849 uintptr_t addr) {
850 for (std::map<uint64_t, CrashedProcess::Mapping>::iterator
851 iter = crashinfo->mappings.begin();
852 iter != crashinfo->mappings.end();
853 ++iter) {
854 if (addr >= iter->second.start_address &&
855 addr < iter->second.end_address) {
856 CrashedProcess::Mapping mapping = iter->second;
857 if ((addr & ~4095) != iter->second.start_address) {
858 // If there are memory pages in the mapping prior to where the
859 // data starts, truncate the existing mapping so that it ends with
860 // the page immediately preceding the data region.
861 iter->second.end_address = addr & ~4095;
862 if (!mapping.filename.empty()) {
863 // "mapping" is a copy of "iter->second". We are splitting the
864 // existing mapping into two separate ones when we write the data
865 // to the core file. The first one does not have any associated
866 // data in the core file, the second one is backed by data that is
867 // included with the core file.
868 // If this mapping wasn't supposed to be anonymous, then we also
869 // have to update the file offset upon splitting the mapping.
870 mapping.offset += iter->second.end_address -
871 iter->second.start_address;
872 }
873 }
874 // Create a new mapping that contains the data contents. We often
875 // limit the amount of data that is actually written to the core
876 // file. But it is OK if the mapping itself extends past the end of
877 // the data.
878 mapping.start_address = addr & ~4095;
879 mapping.data.assign(addr & 4095, 0).append(data);
880 mapping.data.append(-mapping.data.size() & 4095, 0);
881 crashinfo->mappings[mapping.start_address] = mapping;
882 return;
883 }
884 }
885 // Didn't find a suitable existing mapping for the data. Create a new one.
886 CrashedProcess::Mapping mapping;
887 mapping.permissions = PF_R | PF_W;
888 mapping.start_address = addr & ~4095;
889 mapping.end_address =
890 (addr + data.size() + 4095) & ~4095;
891 mapping.data.assign(addr & 4095, 0).append(data);
892 mapping.data.append(-mapping.data.size() & 4095, 0);
893 crashinfo->mappings[mapping.start_address] = mapping;
894 }
895
896 static void
AugmentMappings(CrashedProcess * crashinfo,const MinidumpMemoryRange & full_file)897 AugmentMappings(CrashedProcess* crashinfo,
898 const MinidumpMemoryRange& full_file) {
899 // For each thread, find the memory mapping that matches the thread's stack.
900 // Then adjust the mapping to include the stack dump.
901 for (unsigned i = 0; i < crashinfo->threads.size(); ++i) {
902 const CrashedProcess::Thread& thread = crashinfo->threads[i];
903 AddDataToMapping(crashinfo,
904 std::string((char *)thread.stack, thread.stack_length),
905 thread.stack_addr);
906 }
907
908 // Create a new link map with information about DSOs. We move this map to
909 // the beginning of the address space, as this area should always be
910 // available.
911 static const uintptr_t start_addr = 4096;
912 std::string data;
913 struct r_debug debug = { 0 };
914 debug.r_version = crashinfo->debug.version;
915 debug.r_brk = (ElfW(Addr))crashinfo->debug.brk;
916 debug.r_state = r_debug::RT_CONSISTENT;
917 debug.r_ldbase = (ElfW(Addr))crashinfo->debug.ldbase;
918 debug.r_map = crashinfo->debug.dso_count > 0 ?
919 (struct link_map*)(start_addr + sizeof(debug)) : 0;
920 data.append((char*)&debug, sizeof(debug));
921
922 struct link_map* prev = 0;
923 for (std::vector<MDRawLinkMap>::iterator iter = crashinfo->link_map.begin();
924 iter != crashinfo->link_map.end();
925 ++iter) {
926 struct link_map link_map = { 0 };
927 link_map.l_addr = (ElfW(Addr))iter->addr;
928 link_map.l_name = (char*)(start_addr + data.size() + sizeof(link_map));
929 link_map.l_ld = (ElfW(Dyn)*)iter->ld;
930 link_map.l_prev = prev;
931 prev = (struct link_map*)(start_addr + data.size());
932 std::string filename = full_file.GetAsciiMDString(iter->name);
933
934 // Look up signature for this filename. If available, change filename
935 // to point to GUID, instead.
936 std::map<uintptr_t, std::string>::const_iterator guid =
937 crashinfo->signatures.find((uintptr_t)iter->addr);
938 if (guid != crashinfo->signatures.end()) {
939 filename = guid->second;
940 }
941
942 if (std::distance(iter, crashinfo->link_map.end()) == 1) {
943 link_map.l_next = 0;
944 } else {
945 link_map.l_next = (struct link_map*)(start_addr + data.size() +
946 sizeof(link_map) +
947 ((filename.size() + 8) & ~7));
948 }
949 data.append((char*)&link_map, sizeof(link_map));
950 data.append(filename);
951 data.append(8 - (filename.size() & 7), 0);
952 }
953 AddDataToMapping(crashinfo, data, start_addr);
954
955 // Map the page containing the _DYNAMIC array
956 if (!crashinfo->dynamic_data.empty()) {
957 // Make _DYNAMIC DT_DEBUG entry point to our link map
958 for (int i = 0;; ++i) {
959 ElfW(Dyn) dyn;
960 if ((i+1)*sizeof(dyn) > crashinfo->dynamic_data.length()) {
961 no_dt_debug:
962 if (verbose) {
963 fprintf(stderr, "No DT_DEBUG entry found\n");
964 }
965 return;
966 }
967 memcpy(&dyn, crashinfo->dynamic_data.c_str() + i*sizeof(dyn),
968 sizeof(dyn));
969 if (dyn.d_tag == DT_DEBUG) {
970 crashinfo->dynamic_data.replace(i*sizeof(dyn) +
971 offsetof(ElfW(Dyn), d_un.d_ptr),
972 sizeof(start_addr),
973 (char*)&start_addr, sizeof(start_addr));
974 break;
975 } else if (dyn.d_tag == DT_NULL) {
976 goto no_dt_debug;
977 }
978 }
979 AddDataToMapping(crashinfo, crashinfo->dynamic_data,
980 (uintptr_t)crashinfo->debug.dynamic);
981 }
982 }
983
984 int
main(int argc,char ** argv)985 main(int argc, char** argv) {
986 int argi = 1;
987 while (argi < argc && argv[argi][0] == '-') {
988 if (!strcmp(argv[argi], "-v")) {
989 verbose = true;
990 } else if (!strcmp(argv[argi], "--sobasedir")) {
991 argi++;
992 if (argi >= argc) {
993 fprintf(stderr, "--sobasedir expects an argument.");
994 return usage(argv[0]);
995 }
996
997 g_custom_so_basedir = argv[argi];
998 } else {
999 return usage(argv[0]);
1000 }
1001 argi++;
1002 }
1003
1004 if (argc != argi + 1)
1005 return usage(argv[0]);
1006
1007 MemoryMappedFile mapped_file(argv[argi], 0);
1008 if (!mapped_file.data()) {
1009 fprintf(stderr, "Failed to mmap dump file\n");
1010 return 1;
1011 }
1012
1013 MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
1014
1015 const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
1016
1017 CrashedProcess crashinfo;
1018
1019 // Always check the system info first, as that allows us to tell whether
1020 // this is a minidump file that is compatible with our converter.
1021 bool ok = false;
1022 for (unsigned i = 0; i < header->stream_count; ++i) {
1023 const MDRawDirectory* dirent =
1024 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1025 switch (dirent->stream_type) {
1026 case MD_SYSTEM_INFO_STREAM:
1027 ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1028 ok = true;
1029 break;
1030 default:
1031 break;
1032 }
1033 }
1034 if (!ok) {
1035 fprintf(stderr, "Cannot determine input file format.\n");
1036 _exit(1);
1037 }
1038
1039 for (unsigned i = 0; i < header->stream_count; ++i) {
1040 const MDRawDirectory* dirent =
1041 dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
1042 switch (dirent->stream_type) {
1043 case MD_THREAD_LIST_STREAM:
1044 ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
1045 break;
1046 case MD_LINUX_CPU_INFO:
1047 ParseCPUInfo(&crashinfo, dump.Subrange(dirent->location));
1048 break;
1049 case MD_LINUX_PROC_STATUS:
1050 ParseProcessStatus(&crashinfo, dump.Subrange(dirent->location));
1051 break;
1052 case MD_LINUX_LSB_RELEASE:
1053 ParseLSBRelease(&crashinfo, dump.Subrange(dirent->location));
1054 break;
1055 case MD_LINUX_ENVIRON:
1056 ParseEnvironment(&crashinfo, dump.Subrange(dirent->location));
1057 break;
1058 case MD_LINUX_MAPS:
1059 ParseMaps(&crashinfo, dump.Subrange(dirent->location));
1060 break;
1061 case MD_LINUX_AUXV:
1062 ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
1063 break;
1064 case MD_LINUX_CMD_LINE:
1065 ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
1066 break;
1067 case MD_LINUX_DSO_DEBUG:
1068 ParseDSODebugInfo(&crashinfo, dump.Subrange(dirent->location), dump);
1069 break;
1070 case MD_EXCEPTION_STREAM:
1071 ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
1072 break;
1073 case MD_MODULE_LIST_STREAM:
1074 ParseModuleStream(&crashinfo, dump.Subrange(dirent->location), dump);
1075 break;
1076 default:
1077 if (verbose)
1078 fprintf(stderr, "Skipping %x\n", dirent->stream_type);
1079 }
1080 }
1081
1082 AugmentMappings(&crashinfo, dump);
1083
1084 // Write the ELF header. The file will look like:
1085 // ELF header
1086 // Phdr for the PT_NOTE
1087 // Phdr for each of the thread stacks
1088 // PT_NOTE
1089 // each of the thread stacks
1090 Ehdr ehdr;
1091 memset(&ehdr, 0, sizeof(Ehdr));
1092 ehdr.e_ident[0] = ELFMAG0;
1093 ehdr.e_ident[1] = ELFMAG1;
1094 ehdr.e_ident[2] = ELFMAG2;
1095 ehdr.e_ident[3] = ELFMAG3;
1096 ehdr.e_ident[4] = ELF_CLASS;
1097 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
1098 ehdr.e_ident[6] = EV_CURRENT;
1099 ehdr.e_type = ET_CORE;
1100 ehdr.e_machine = ELF_ARCH;
1101 ehdr.e_version = EV_CURRENT;
1102 ehdr.e_phoff = sizeof(Ehdr);
1103 ehdr.e_ehsize = sizeof(Ehdr);
1104 ehdr.e_phentsize= sizeof(Phdr);
1105 ehdr.e_phnum = 1 + // PT_NOTE
1106 crashinfo.mappings.size(); // memory mappings
1107 ehdr.e_shentsize= sizeof(Shdr);
1108 if (!writea(1, &ehdr, sizeof(Ehdr)))
1109 return 1;
1110
1111 size_t offset = sizeof(Ehdr) + ehdr.e_phnum * sizeof(Phdr);
1112 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
1113 // sizeof(Nhdr) + 8 + sizeof(user) +
1114 sizeof(Nhdr) + 8 + crashinfo.auxv_length +
1115 crashinfo.threads.size() * (
1116 (sizeof(Nhdr) + 8 + sizeof(prstatus))
1117 #if defined(__i386__) || defined(__x86_64__)
1118 + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct)
1119 #endif
1120 #if defined(__i386__)
1121 + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
1122 #endif
1123 );
1124
1125 Phdr phdr;
1126 memset(&phdr, 0, sizeof(Phdr));
1127 phdr.p_type = PT_NOTE;
1128 phdr.p_offset = offset;
1129 phdr.p_filesz = filesz;
1130 if (!writea(1, &phdr, sizeof(phdr)))
1131 return 1;
1132
1133 phdr.p_type = PT_LOAD;
1134 phdr.p_align = 4096;
1135 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
1136 if (note_align == phdr.p_align)
1137 note_align = 0;
1138 offset += note_align;
1139
1140 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1141 crashinfo.mappings.begin();
1142 iter != crashinfo.mappings.end(); ++iter) {
1143 const CrashedProcess::Mapping& mapping = iter->second;
1144 if (mapping.permissions == 0xFFFFFFFF) {
1145 // This is a map that we found in MD_MODULE_LIST_STREAM (as opposed to
1146 // MD_LINUX_MAPS). It lacks some of the information that we would like
1147 // to include.
1148 phdr.p_flags = PF_R;
1149 } else {
1150 phdr.p_flags = mapping.permissions;
1151 }
1152 phdr.p_vaddr = mapping.start_address;
1153 phdr.p_memsz = mapping.end_address - mapping.start_address;
1154 if (mapping.data.size()) {
1155 offset += filesz;
1156 filesz = mapping.data.size();
1157 phdr.p_filesz = mapping.data.size();
1158 phdr.p_offset = offset;
1159 } else {
1160 phdr.p_filesz = 0;
1161 phdr.p_offset = 0;
1162 }
1163 if (!writea(1, &phdr, sizeof(phdr)))
1164 return 1;
1165 }
1166
1167 Nhdr nhdr;
1168 memset(&nhdr, 0, sizeof(nhdr));
1169 nhdr.n_namesz = 5;
1170 nhdr.n_descsz = sizeof(prpsinfo);
1171 nhdr.n_type = NT_PRPSINFO;
1172 if (!writea(1, &nhdr, sizeof(nhdr)) ||
1173 !writea(1, "CORE\0\0\0\0", 8) ||
1174 !writea(1, &crashinfo.prps, sizeof(prpsinfo))) {
1175 return 1;
1176 }
1177
1178 nhdr.n_descsz = crashinfo.auxv_length;
1179 nhdr.n_type = NT_AUXV;
1180 if (!writea(1, &nhdr, sizeof(nhdr)) ||
1181 !writea(1, "CORE\0\0\0\0", 8) ||
1182 !writea(1, crashinfo.auxv, crashinfo.auxv_length)) {
1183 return 1;
1184 }
1185
1186 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1187 if (crashinfo.threads[i].tid == crashinfo.crashing_tid) {
1188 WriteThread(crashinfo.threads[i], crashinfo.fatal_signal);
1189 break;
1190 }
1191 }
1192
1193 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
1194 if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
1195 WriteThread(crashinfo.threads[i], 0);
1196 }
1197
1198 if (note_align) {
1199 google_breakpad::scoped_array<char> scratch(new char[note_align]);
1200 memset(scratch.get(), 0, note_align);
1201 if (!writea(1, scratch.get(), note_align))
1202 return 1;
1203 }
1204
1205 for (std::map<uint64_t, CrashedProcess::Mapping>::const_iterator iter =
1206 crashinfo.mappings.begin();
1207 iter != crashinfo.mappings.end(); ++iter) {
1208 const CrashedProcess::Mapping& mapping = iter->second;
1209 if (mapping.data.size()) {
1210 if (!writea(1, mapping.data.c_str(), mapping.data.size()))
1211 return 1;
1212 }
1213 }
1214
1215 return 0;
1216 }
1217