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