| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009, Google Inc. | |
| 2 // All rights reserved. | |
| 3 // | |
| 4 // Redistribution and use in source and binary forms, with or without | |
| 5 // modification, are permitted provided that the following conditions are | |
| 6 // met: | |
| 7 // | |
| 8 // * Redistributions of source code must retain the above copyright | |
| 9 // notice, this list of conditions and the following disclaimer. | |
| 10 // * Redistributions in binary form must reproduce the above | |
| 11 // copyright notice, this list of conditions and the following disclaimer | |
| 12 // in the documentation and/or other materials provided with the | |
| 13 // distribution. | |
| 14 // * Neither the name of Google Inc. nor the names of its | |
| 15 // contributors may be used to endorse or promote products derived from | |
| 16 // this software without specific prior written permission. | |
| 17 // | |
| 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 | |
| 30 // Converts a minidump file to a core file which gdb can read. | |
| 31 // Large parts lifted from the userspace core dumper: | |
| 32 // http://code.google.com/p/google-coredumper/ | |
| 33 // | |
| 34 // Usage: minidump-2-core 1234.dmp > core | |
| 35 | |
| 36 #include <vector> | |
| 37 | |
| 38 #include <stdio.h> | |
| 39 #include <string.h> | |
| 40 | |
| 41 #include <elf.h> | |
| 42 #include <errno.h> | |
| 43 #include <unistd.h> | |
| 44 #include <fcntl.h> | |
| 45 #include <sys/user.h> | |
| 46 #include <sys/mman.h> | |
| 47 | |
| 48 #include "google_breakpad/common/minidump_format.h" | |
| 49 #include "google_breakpad/common/minidump_cpu_x86.h" | |
| 50 #include "breakpad/linux/linux_syscall_support.h" | |
| 51 #include "breakpad/linux/minidump_format_linux.h" | |
| 52 | |
| 53 #if __WORDSIZE == 64 | |
| 54 #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 | |
| 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 | |
| 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_ARCH_3__) | |
| 75 #define ELF_ARCH EM_ARM | |
| 76 #elif defined(__mips__) | |
| 77 #define ELF_ARCH EM_MIPS | |
| 78 #endif | |
| 79 | |
| 80 static int usage(const char* argv0) { | |
| 81 fprintf(stderr, "Usage: %s <minidump file>\n", argv0); | |
| 82 return 1; | |
| 83 } | |
| 84 | |
| 85 // Write all of the given buffer, handling short writes and EINTR. Return true | |
| 86 // iff successful. | |
| 87 static bool | |
| 88 writea(int fd, const void* idata, size_t length) { | |
| 89 const uint8_t* data = (const uint8_t*) idata; | |
| 90 | |
| 91 size_t done = 0; | |
| 92 while (done < length) { | |
| 93 ssize_t r; | |
| 94 do { | |
| 95 r = write(fd, data + done, length - done); | |
| 96 } while (r == -1 && errno == EINTR); | |
| 97 | |
| 98 if (r < 1) | |
| 99 return false; | |
| 100 done += r; | |
| 101 } | |
| 102 | |
| 103 return true; | |
| 104 } | |
| 105 | |
| 106 // A range of a mmaped file. | |
| 107 class MMappedRange { | |
| 108 public: | |
| 109 MMappedRange(const void* data, size_t length) | |
| 110 : data_(reinterpret_cast<const uint8_t*>(data)), | |
| 111 length_(length) { | |
| 112 } | |
| 113 | |
| 114 // Get an object of |length| bytes at |offset| and return a pointer to it | |
| 115 // unless it's out of bounds. | |
| 116 const void* GetObject(size_t offset, size_t length) { | |
| 117 if (offset + length < offset) | |
| 118 return NULL; | |
| 119 if (offset + length > length_) | |
| 120 return NULL; | |
| 121 return data_ + offset; | |
| 122 } | |
| 123 | |
| 124 // Get element |index| of an array of objects of length |length| starting at | |
| 125 // |offset| bytes. Return NULL if out of bounds. | |
| 126 const void* GetArrayElement(size_t offset, size_t length, unsigned index) { | |
| 127 const size_t element_offset = offset + index * length; | |
| 128 return GetObject(element_offset, length); | |
| 129 } | |
| 130 | |
| 131 // Return a new range which is a subset of this range. | |
| 132 MMappedRange Subrange(const MDLocationDescriptor& location) const { | |
| 133 if (location.rva > length_ || | |
| 134 location.rva + location.data_size < location.rva || | |
| 135 location.rva + location.data_size > length_) { | |
| 136 return MMappedRange(NULL, 0); | |
| 137 } | |
| 138 | |
| 139 return MMappedRange(data_ + location.rva, location.data_size); | |
| 140 } | |
| 141 | |
| 142 const uint8_t* data() const { return data_; } | |
| 143 size_t length() const { return length_; } | |
| 144 | |
| 145 private: | |
| 146 const uint8_t* const data_; | |
| 147 const size_t length_; | |
| 148 }; | |
| 149 | |
| 150 /* Dynamically determines the byte sex of the system. Returns non-zero | |
| 151 * for big-endian machines. | |
| 152 */ | |
| 153 static inline int sex() { | |
| 154 int probe = 1; | |
| 155 return !*(char *)&probe; | |
| 156 } | |
| 157 | |
| 158 typedef struct elf_timeval { /* Time value with microsecond resolution */ | |
| 159 long tv_sec; /* Seconds */ | |
| 160 long tv_usec; /* Microseconds */ | |
| 161 } elf_timeval; | |
| 162 | |
| 163 typedef struct elf_siginfo { /* Information about signal (unused) */ | |
| 164 int32_t si_signo; /* Signal number */ | |
| 165 int32_t si_code; /* Extra code */ | |
| 166 int32_t si_errno; /* Errno */ | |
| 167 } elf_siginfo; | |
| 168 | |
| 169 typedef struct prstatus { /* Information about thread; includes CPU reg*/ | |
| 170 elf_siginfo pr_info; /* Info associated with signal */ | |
| 171 uint16_t pr_cursig; /* Current signal */ | |
| 172 unsigned long pr_sigpend; /* Set of pending signals */ | |
| 173 unsigned long pr_sighold; /* Set of held signals */ | |
| 174 pid_t pr_pid; /* Process ID */ | |
| 175 pid_t pr_ppid; /* Parent's process ID */ | |
| 176 pid_t pr_pgrp; /* Group ID */ | |
| 177 pid_t pr_sid; /* Session ID */ | |
| 178 elf_timeval pr_utime; /* User time */ | |
| 179 elf_timeval pr_stime; /* System time */ | |
| 180 elf_timeval pr_cutime; /* Cumulative user time */ | |
| 181 elf_timeval pr_cstime; /* Cumulative system time */ | |
| 182 user_regs_struct pr_reg; /* CPU registers */ | |
| 183 uint32_t pr_fpvalid; /* True if math co-processor being used */ | |
| 184 } prstatus; | |
| 185 | |
| 186 typedef struct prpsinfo { /* Information about process */ | |
| 187 unsigned char pr_state; /* Numeric process state */ | |
| 188 char pr_sname; /* Char for pr_state */ | |
| 189 unsigned char pr_zomb; /* Zombie */ | |
| 190 signed char pr_nice; /* Nice val */ | |
| 191 unsigned long pr_flag; /* Flags */ | |
| 192 #if defined(__x86_64__) || defined(__mips__) | |
| 193 uint32_t pr_uid; /* User ID */ | |
| 194 uint32_t pr_gid; /* Group ID */ | |
| 195 #else | |
| 196 uint16_t pr_uid; /* User ID */ | |
| 197 uint16_t pr_gid; /* Group ID */ | |
| 198 #endif | |
| 199 pid_t pr_pid; /* Process ID */ | |
| 200 pid_t pr_ppid; /* Parent's process ID */ | |
| 201 pid_t pr_pgrp; /* Group ID */ | |
| 202 pid_t pr_sid; /* Session ID */ | |
| 203 char pr_fname[16]; /* Filename of executable */ | |
| 204 char pr_psargs[80]; /* Initial part of arg list */ | |
| 205 } prpsinfo; | |
| 206 | |
| 207 // We parse the minidump file and keep the parsed information in this structure. | |
| 208 struct CrashedProcess { | |
| 209 CrashedProcess() | |
| 210 : crashing_tid(-1), | |
| 211 auxv(NULL), | |
| 212 auxv_length(0) { | |
| 213 memset(&prps, 0, sizeof(prps)); | |
| 214 prps.pr_sname = 'R'; | |
| 215 } | |
| 216 | |
| 217 struct Mapping { | |
| 218 uint64_t start_address, end_address; | |
| 219 }; | |
| 220 std::vector<Mapping> mappings; | |
| 221 | |
| 222 pid_t crashing_tid; | |
| 223 int fatal_signal; | |
| 224 | |
| 225 struct Thread { | |
| 226 pid_t tid; | |
| 227 user_regs_struct regs; | |
| 228 user_fpregs_struct fpregs; | |
| 229 user_fpxregs_struct fpxregs; | |
| 230 uintptr_t stack_addr; | |
| 231 const uint8_t* stack; | |
| 232 size_t stack_length; | |
| 233 }; | |
| 234 std::vector<Thread> threads; | |
| 235 | |
| 236 const uint8_t* auxv; | |
| 237 size_t auxv_length; | |
| 238 | |
| 239 prpsinfo prps; | |
| 240 }; | |
| 241 | |
| 242 static uint32_t | |
| 243 U32(const uint8_t* data) { | |
| 244 uint32_t v; | |
| 245 memcpy(&v, data, sizeof(v)); | |
| 246 return v; | |
| 247 } | |
| 248 | |
| 249 static uint16_t | |
| 250 U16(const uint8_t* data) { | |
| 251 uint16_t v; | |
| 252 memcpy(&v, data, sizeof(v)); | |
| 253 return v; | |
| 254 } | |
| 255 | |
| 256 #if defined(__i386__) | |
| 257 static void | |
| 258 ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) { | |
| 259 const MDRawContextX86* rawregs = | |
| 260 (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86)); | |
| 261 | |
| 262 thread->regs.ebx = rawregs->ebx; | |
| 263 thread->regs.ecx = rawregs->ecx; | |
| 264 thread->regs.edx = rawregs->edx; | |
| 265 thread->regs.esi = rawregs->esi; | |
| 266 thread->regs.edi = rawregs->edi; | |
| 267 thread->regs.ebp = rawregs->ebp; | |
| 268 thread->regs.eax = rawregs->eax; | |
| 269 thread->regs.xds = rawregs->ds; | |
| 270 thread->regs.xes = rawregs->es; | |
| 271 thread->regs.xfs = rawregs->fs; | |
| 272 thread->regs.xgs = rawregs->gs; | |
| 273 thread->regs.orig_eax = rawregs->eax; | |
| 274 thread->regs.eip = rawregs->eip; | |
| 275 thread->regs.xcs = rawregs->cs; | |
| 276 thread->regs.eflags = rawregs->eflags; | |
| 277 thread->regs.esp = rawregs->esp; | |
| 278 thread->regs.xss = rawregs->ss; | |
| 279 | |
| 280 thread->fpregs.cwd = rawregs->float_save.control_word; | |
| 281 thread->fpregs.swd = rawregs->float_save.status_word; | |
| 282 thread->fpregs.twd = rawregs->float_save.tag_word; | |
| 283 thread->fpregs.fip = rawregs->float_save.error_offset; | |
| 284 thread->fpregs.fcs = rawregs->float_save.error_selector; | |
| 285 thread->fpregs.foo = rawregs->float_save.data_offset; | |
| 286 thread->fpregs.fos = rawregs->float_save.data_selector; | |
| 287 memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, | |
| 288 10 * 8); | |
| 289 | |
| 290 thread->fpxregs.cwd = rawregs->float_save.control_word; | |
| 291 thread->fpxregs.swd = rawregs->float_save.status_word; | |
| 292 thread->fpxregs.twd = rawregs->float_save.tag_word; | |
| 293 thread->fpxregs.fop = U16(rawregs->extended_registers + 6); | |
| 294 thread->fpxregs.fip = U16(rawregs->extended_registers + 8); | |
| 295 thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); | |
| 296 thread->fpxregs.foo = U16(rawregs->extended_registers + 16); | |
| 297 thread->fpxregs.fos = U16(rawregs->extended_registers + 20); | |
| 298 thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); | |
| 299 memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); | |
| 300 memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); | |
| 301 } | |
| 302 #else | |
| 303 #error "This code has not been ported to your platform yet" | |
| 304 #endif | |
| 305 | |
| 306 static void | |
| 307 ParseThreadList(CrashedProcess* crashinfo, MMappedRange range, | |
| 308 const MMappedRange& full_file) { | |
| 309 const uint32_t num_threads = | |
| 310 *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); | |
| 311 for (unsigned i = 0; i < num_threads; ++i) { | |
| 312 CrashedProcess::Thread thread; | |
| 313 memset(&thread, 0, sizeof(thread)); | |
| 314 const MDRawThread* rawthread = | |
| 315 (MDRawThread*) range.GetArrayElement(sizeof(uint32_t), | |
| 316 sizeof(MDRawThread), i); | |
| 317 thread.tid = rawthread->thread_id; | |
| 318 thread.stack_addr = rawthread->stack.start_of_memory_range; | |
| 319 MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory); | |
| 320 thread.stack = stack_range.data(); | |
| 321 thread.stack_length = rawthread->stack.memory.data_size; | |
| 322 | |
| 323 ParseThreadRegisters(&thread, | |
| 324 full_file.Subrange(rawthread->thread_context)); | |
| 325 | |
| 326 crashinfo->threads.push_back(thread); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 static void | |
| 331 ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) { | |
| 332 crashinfo->auxv = range.data(); | |
| 333 crashinfo->auxv_length = range.length(); | |
| 334 } | |
| 335 | |
| 336 static void | |
| 337 ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) { | |
| 338 const char* cmdline = (const char*) range.data(); | |
| 339 for (size_t i = 0; i < range.length(); ++i) { | |
| 340 if (cmdline[i] == 0) { | |
| 341 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; | |
| 343 memset(crashinfo->prps.pr_fname, 0, fname_len + 1); | |
| 344 memset(crashinfo->prps.pr_psargs, 0, args_len + 1); | |
| 345 const char* binary_name = strrchr(cmdline, '/'); | |
| 346 if (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); | |
| 351 } else { | |
| 352 memcpy(crashinfo->prps.pr_fname, cmdline, | |
| 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); | |
| 359 for (unsigned i = 0; i < len; ++i) { | |
| 360 if (crashinfo->prps.pr_psargs[i] == 0) | |
| 361 crashinfo->prps.pr_psargs[i] = ' '; | |
| 362 } | |
| 363 } | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 static void | |
| 368 ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) { | |
| 369 const MDRawExceptionStream* exp = | |
| 370 (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream)); | |
| 371 crashinfo->crashing_tid = exp->thread_id; | |
| 372 crashinfo->fatal_signal = (int) exp->exception_record.exception_code; | |
| 373 } | |
| 374 | |
| 375 static bool | |
| 376 WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { | |
| 377 struct prstatus pr; | |
| 378 memset(&pr, 0, sizeof(pr)); | |
| 379 | |
| 380 pr.pr_info.si_signo = fatal_signal; | |
| 381 pr.pr_cursig = fatal_signal; | |
| 382 pr.pr_pid = thread.tid; | |
| 383 memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); | |
| 384 | |
| 385 Nhdr nhdr; | |
| 386 memset(&nhdr, 0, sizeof(nhdr)); | |
| 387 nhdr.n_namesz = 5; | |
| 388 nhdr.n_descsz = sizeof(struct prstatus); | |
| 389 nhdr.n_type = NT_PRSTATUS; | |
| 390 if (!writea(1, &nhdr, sizeof(nhdr)) || | |
| 391 !writea(1, "CORE\0\0\0\0", 8) || | |
| 392 !writea(1, &pr, sizeof(struct prstatus))) { | |
| 393 return false; | |
| 394 } | |
| 395 | |
| 396 nhdr.n_descsz = sizeof(user_fpregs_struct); | |
| 397 nhdr.n_type = NT_FPREGSET; | |
| 398 if (!writea(1, &nhdr, sizeof(nhdr)) || | |
| 399 !writea(1, "CORE\0\0\0\0", 8) || | |
| 400 !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { | |
| 401 return false; | |
| 402 } | |
| 403 | |
| 404 nhdr.n_descsz = sizeof(user_fpxregs_struct); | |
| 405 nhdr.n_type = NT_PRXFPREG; | |
| 406 if (!writea(1, &nhdr, sizeof(nhdr)) || | |
| 407 !writea(1, "LINUX\0\0\0", 8) || | |
| 408 !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { | |
| 409 return false; | |
| 410 } | |
| 411 | |
| 412 return true; | |
| 413 } | |
| 414 | |
| 415 static void | |
| 416 ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) { | |
| 417 const uint32_t num_mappings = | |
| 418 *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); | |
| 419 for (unsigned i = 0; i < num_mappings; ++i) { | |
| 420 CrashedProcess::Mapping mapping; | |
| 421 const MDRawModule* rawmodule = | |
| 422 (MDRawModule*) range.GetArrayElement(sizeof(uint32_t), | |
| 423 MD_MODULE_SIZE, i); | |
| 424 mapping.start_address = rawmodule->base_of_image; | |
| 425 mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; | |
| 426 | |
| 427 crashinfo->mappings.push_back(mapping); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 int | |
| 432 main(int argc, char** argv) { | |
| 433 if (argc != 2) | |
| 434 return usage(argv[0]); | |
| 435 | |
| 436 const int fd = open(argv[1], O_RDONLY); | |
| 437 if (fd < 0) | |
| 438 return usage(argv[0]); | |
| 439 | |
| 440 struct stat st; | |
| 441 fstat(fd, &st); | |
| 442 | |
| 443 const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
| 444 close(fd); | |
| 445 if (bytes == MAP_FAILED) { | |
| 446 perror("Failed to mmap dump file"); | |
| 447 return 1; | |
| 448 } | |
| 449 | |
| 450 MMappedRange dump(bytes, st.st_size); | |
| 451 | |
| 452 const MDRawHeader* header = | |
| 453 (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader)); | |
| 454 | |
| 455 CrashedProcess crashinfo; | |
| 456 | |
| 457 for (unsigned i = 0; i < header->stream_count; ++i) { | |
| 458 const MDRawDirectory* dirent = | |
| 459 (const MDRawDirectory*) dump.GetArrayElement( | |
| 460 header->stream_directory_rva, sizeof(MDRawDirectory), i); | |
| 461 switch (dirent->stream_type) { | |
| 462 case MD_THREAD_LIST_STREAM: | |
| 463 ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); | |
| 464 break; | |
| 465 case MD_LINUX_AUXV: | |
| 466 ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); | |
| 467 break; | |
| 468 case MD_LINUX_CMD_LINE: | |
| 469 ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); | |
| 470 break; | |
| 471 case MD_EXCEPTION_STREAM: | |
| 472 ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); | |
| 473 break; | |
| 474 case MD_MODULE_LIST_STREAM: | |
| 475 ParseModuleStream(&crashinfo, dump.Subrange(dirent->location)); | |
| 476 default: | |
| 477 fprintf(stderr, "Skipping %x\n", dirent->stream_type); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 // Write the ELF header. The file will look like: | |
| 482 // ELF header | |
| 483 // Phdr for the PT_NOTE | |
| 484 // Phdr for each of the thread stacks | |
| 485 // PT_NOTE | |
| 486 // each of the thread stacks | |
| 487 Ehdr ehdr; | |
| 488 memset(&ehdr, 0, sizeof(Ehdr)); | |
| 489 ehdr.e_ident[0] = ELFMAG0; | |
| 490 ehdr.e_ident[1] = ELFMAG1; | |
| 491 ehdr.e_ident[2] = ELFMAG2; | |
| 492 ehdr.e_ident[3] = ELFMAG3; | |
| 493 ehdr.e_ident[4] = ELF_CLASS; | |
| 494 ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; | |
| 495 ehdr.e_ident[6] = EV_CURRENT; | |
| 496 ehdr.e_type = ET_CORE; | |
| 497 ehdr.e_machine = ELF_ARCH; | |
| 498 ehdr.e_version = EV_CURRENT; | |
| 499 ehdr.e_phoff = sizeof(Ehdr); | |
| 500 ehdr.e_ehsize = sizeof(Ehdr); | |
| 501 ehdr.e_phentsize= sizeof(Phdr); | |
| 502 ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size(); | |
| 503 ehdr.e_shentsize= sizeof(Shdr); | |
| 504 if (!writea(1, &ehdr, sizeof(Ehdr))) | |
| 505 return 1; | |
| 506 | |
| 507 size_t offset = sizeof(Ehdr) + | |
| 508 (1 + crashinfo.threads.size() + | |
| 509 crashinfo.mappings.size()) * sizeof(Phdr); | |
| 510 size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + | |
| 511 // sizeof(Nhdr) + 8 + sizeof(user) + | |
| 512 sizeof(Nhdr) + 8 + crashinfo.auxv_length + | |
| 513 crashinfo.threads.size() * ( | |
| 514 (sizeof(Nhdr) + 8 + sizeof(prstatus)) + | |
| 515 sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) + | |
| 516 sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)); | |
| 517 | |
| 518 Phdr phdr; | |
| 519 memset(&phdr, 0, sizeof(Phdr)); | |
| 520 phdr.p_type = PT_NOTE; | |
| 521 phdr.p_offset = offset; | |
| 522 phdr.p_filesz = filesz; | |
| 523 if (!writea(1, &phdr, sizeof(phdr))) | |
| 524 return 1; | |
| 525 | |
| 526 phdr.p_type = PT_LOAD; | |
| 527 phdr.p_align = getpagesize(); | |
| 528 size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); | |
| 529 if (note_align == phdr.p_align) | |
| 530 note_align = 0; | |
| 531 offset += note_align; | |
| 532 | |
| 533 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { | |
| 534 const CrashedProcess::Thread& thread = crashinfo.threads[i]; | |
| 535 offset += filesz; | |
| 536 filesz = thread.stack_length; | |
| 537 phdr.p_offset = offset; | |
| 538 phdr.p_vaddr = thread.stack_addr; | |
| 539 phdr.p_filesz = phdr.p_memsz = filesz; | |
| 540 phdr.p_flags = PF_R | PF_W; | |
| 541 if (!writea(1, &phdr, sizeof(phdr))) | |
| 542 return 1; | |
| 543 } | |
| 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 | |
| 556 Nhdr nhdr; | |
| 557 memset(&nhdr, 0, sizeof(nhdr)); | |
| 558 nhdr.n_namesz = 5; | |
| 559 nhdr.n_descsz = sizeof(prpsinfo); | |
| 560 nhdr.n_type = NT_PRPSINFO; | |
| 561 if (!writea(1, &nhdr, sizeof(nhdr)) || | |
| 562 !writea(1, "CORE\0\0\0\0", 8) || | |
| 563 !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { | |
| 564 return 1; | |
| 565 } | |
| 566 | |
| 567 nhdr.n_descsz = crashinfo.auxv_length; | |
| 568 nhdr.n_type = NT_AUXV; | |
| 569 if (!writea(1, &nhdr, sizeof(nhdr)) || | |
| 570 !writea(1, "CORE\0\0\0\0", 8) || | |
| 571 !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) { | |
| 572 return 1; | |
| 573 } | |
| 574 | |
| 575 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { | |
| 576 if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { | |
| 577 WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); | |
| 578 break; | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { | |
| 583 if (crashinfo.threads[i].tid != crashinfo.crashing_tid) | |
| 584 WriteThread(crashinfo.threads[i], 0); | |
| 585 } | |
| 586 | |
| 587 if (note_align) { | |
| 588 char scratch[note_align]; | |
| 589 memset(scratch, 0, sizeof(scratch)); | |
| 590 if (!writea(1, scratch, sizeof(scratch))) | |
| 591 return 1; | |
| 592 } | |
| 593 | |
| 594 for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { | |
| 595 const CrashedProcess::Thread& thread = crashinfo.threads[i]; | |
| 596 if (!writea(1, thread.stack, thread.stack_length)) | |
| 597 return 1; | |
| 598 } | |
| 599 | |
| 600 munmap(const_cast<void*>(bytes), st.st_size); | |
| 601 | |
| 602 return 0; | |
| 603 } | |
| OLD | NEW |