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