minidump_writer.cc revision cfc8628092e17069d8d6c7068d4f21d9baabe077
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// This code writes out minidump files: 31// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx 32// 33// Minidumps are a Microsoft format which Breakpad uses for recording crash 34// dumps. This code has to run in a compromised environment (the address space 35// may have received SIGSEGV), thus the following rules apply: 36// * You may not enter the dynamic linker. This means that we cannot call 37// any symbols in a shared library (inc libc). Because of this we replace 38// libc functions in linux_libc_support.h. 39// * You may not call syscalls via the libc wrappers. This rule is a subset 40// of the first rule but it bears repeating. We have direct wrappers 41// around the system calls in linux_syscall_support.h. 42// * You may not malloc. There's an alternative allocator in memory.h and 43// a canonical instance in the LinuxDumper object. We use the placement 44// new form to allocate objects and we don't delete them. 45 46#include "client/linux/minidump_writer/minidump_writer.h" 47#include "client/minidump_file_writer-inl.h" 48 49#include <algorithm> 50 51#include <errno.h> 52#include <fcntl.h> 53#if !defined(__ANDROID__) 54#include <link.h> 55#endif 56#include <stdio.h> 57#include <unistd.h> 58#if !defined(__ANDROID__) 59#include <sys/ucontext.h> 60#include <sys/user.h> 61#endif 62#include <sys/utsname.h> 63 64#include "client/minidump_file_writer.h" 65#include "google_breakpad/common/minidump_format.h" 66#include "google_breakpad/common/minidump_cpu_amd64.h" 67#include "google_breakpad/common/minidump_cpu_x86.h" 68 69#if defined(__ANDROID__) 70#include "client/linux/android_link.h" 71#include "client/linux/android_ucontext.h" 72#endif 73#include "client/linux/handler/exception_handler.h" 74#include "client/linux/minidump_writer/line_reader.h" 75#include "client/linux/minidump_writer/linux_dumper.h" 76#include "client/linux/minidump_writer/minidump_extension_linux.h" 77#include "common/linux/linux_libc_support.h" 78#include "third_party/lss/linux_syscall_support.h" 79 80// Minidump defines register structures which are different from the raw 81// structures which we get from the kernel. These are platform specific 82// functions to juggle the ucontext and user structures into minidump format. 83#if defined(__i386) 84typedef MDRawContextX86 RawContextCPU; 85 86// Write a uint16_t to memory 87// out: memory location to write to 88// v: value to write. 89static void U16(void* out, uint16_t v) { 90 memcpy(out, &v, sizeof(v)); 91} 92 93// Write a uint32_t to memory 94// out: memory location to write to 95// v: value to write. 96static void U32(void* out, uint32_t v) { 97 memcpy(out, &v, sizeof(v)); 98} 99 100// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format 101// out: the minidump structure 102// info: the collection of register structures. 103static void CPUFillFromThreadInfo(MDRawContextX86 *out, 104 const google_breakpad::ThreadInfo &info) { 105 out->context_flags = MD_CONTEXT_X86_ALL; 106 107 out->dr0 = info.dregs[0]; 108 out->dr1 = info.dregs[1]; 109 out->dr2 = info.dregs[2]; 110 out->dr3 = info.dregs[3]; 111 // 4 and 5 deliberatly omitted because they aren't included in the minidump 112 // format. 113 out->dr6 = info.dregs[6]; 114 out->dr7 = info.dregs[7]; 115 116 out->gs = info.regs.xgs; 117 out->fs = info.regs.xfs; 118 out->es = info.regs.xes; 119 out->ds = info.regs.xds; 120 121 out->edi = info.regs.edi; 122 out->esi = info.regs.esi; 123 out->ebx = info.regs.ebx; 124 out->edx = info.regs.edx; 125 out->ecx = info.regs.ecx; 126 out->eax = info.regs.eax; 127 128 out->ebp = info.regs.ebp; 129 out->eip = info.regs.eip; 130 out->cs = info.regs.xcs; 131 out->eflags = info.regs.eflags; 132 out->esp = info.regs.esp; 133 out->ss = info.regs.xss; 134 135 out->float_save.control_word = info.fpregs.cwd; 136 out->float_save.status_word = info.fpregs.swd; 137 out->float_save.tag_word = info.fpregs.twd; 138 out->float_save.error_offset = info.fpregs.fip; 139 out->float_save.error_selector = info.fpregs.fcs; 140 out->float_save.data_offset = info.fpregs.foo; 141 out->float_save.data_selector = info.fpregs.fos; 142 143 // 8 registers * 10 bytes per register. 144 memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); 145 146 // This matches the Intel fpsave format. 147 U16(out->extended_registers + 0, info.fpregs.cwd); 148 U16(out->extended_registers + 2, info.fpregs.swd); 149 U16(out->extended_registers + 4, info.fpregs.twd); 150 U16(out->extended_registers + 6, info.fpxregs.fop); 151 U32(out->extended_registers + 8, info.fpxregs.fip); 152 U16(out->extended_registers + 12, info.fpxregs.fcs); 153 U32(out->extended_registers + 16, info.fpregs.foo); 154 U16(out->extended_registers + 20, info.fpregs.fos); 155 U32(out->extended_registers + 24, info.fpxregs.mxcsr); 156 157 memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); 158 memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); 159} 160 161// Juggle an x86 ucontext into minidump format 162// out: the minidump structure 163// info: the collection of register structures. 164static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, 165 const struct _libc_fpstate* fp) { 166 const greg_t* regs = uc->uc_mcontext.gregs; 167 168 out->context_flags = MD_CONTEXT_X86_FULL | 169 MD_CONTEXT_X86_FLOATING_POINT; 170 171 out->gs = regs[REG_GS]; 172 out->fs = regs[REG_FS]; 173 out->es = regs[REG_ES]; 174 out->ds = regs[REG_DS]; 175 176 out->edi = regs[REG_EDI]; 177 out->esi = regs[REG_ESI]; 178 out->ebx = regs[REG_EBX]; 179 out->edx = regs[REG_EDX]; 180 out->ecx = regs[REG_ECX]; 181 out->eax = regs[REG_EAX]; 182 183 out->ebp = regs[REG_EBP]; 184 out->eip = regs[REG_EIP]; 185 out->cs = regs[REG_CS]; 186 out->eflags = regs[REG_EFL]; 187 out->esp = regs[REG_UESP]; 188 out->ss = regs[REG_SS]; 189 190 out->float_save.control_word = fp->cw; 191 out->float_save.status_word = fp->sw; 192 out->float_save.tag_word = fp->tag; 193 out->float_save.error_offset = fp->ipoff; 194 out->float_save.error_selector = fp->cssel; 195 out->float_save.data_offset = fp->dataoff; 196 out->float_save.data_selector = fp->datasel; 197 198 // 8 registers * 10 bytes per register. 199 memcpy(out->float_save.register_area, fp->_st, 10 * 8); 200} 201 202#elif defined(__x86_64) 203typedef MDRawContextAMD64 RawContextCPU; 204 205static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, 206 const google_breakpad::ThreadInfo &info) { 207 out->context_flags = MD_CONTEXT_AMD64_FULL | 208 MD_CONTEXT_AMD64_SEGMENTS; 209 210 out->cs = info.regs.cs; 211 212 out->ds = info.regs.ds; 213 out->es = info.regs.es; 214 out->fs = info.regs.fs; 215 out->gs = info.regs.gs; 216 217 out->ss = info.regs.ss; 218 out->eflags = info.regs.eflags; 219 220 out->dr0 = info.dregs[0]; 221 out->dr1 = info.dregs[1]; 222 out->dr2 = info.dregs[2]; 223 out->dr3 = info.dregs[3]; 224 // 4 and 5 deliberatly omitted because they aren't included in the minidump 225 // format. 226 out->dr6 = info.dregs[6]; 227 out->dr7 = info.dregs[7]; 228 229 out->rax = info.regs.rax; 230 out->rcx = info.regs.rcx; 231 out->rdx = info.regs.rdx; 232 out->rbx = info.regs.rbx; 233 234 out->rsp = info.regs.rsp; 235 236 out->rbp = info.regs.rbp; 237 out->rsi = info.regs.rsi; 238 out->rdi = info.regs.rdi; 239 out->r8 = info.regs.r8; 240 out->r9 = info.regs.r9; 241 out->r10 = info.regs.r10; 242 out->r11 = info.regs.r11; 243 out->r12 = info.regs.r12; 244 out->r13 = info.regs.r13; 245 out->r14 = info.regs.r14; 246 out->r15 = info.regs.r15; 247 248 out->rip = info.regs.rip; 249 250 out->flt_save.control_word = info.fpregs.cwd; 251 out->flt_save.status_word = info.fpregs.swd; 252 out->flt_save.tag_word = info.fpregs.ftw; 253 out->flt_save.error_opcode = info.fpregs.fop; 254 out->flt_save.error_offset = info.fpregs.rip; 255 out->flt_save.error_selector = 0; // We don't have this. 256 out->flt_save.data_offset = info.fpregs.rdp; 257 out->flt_save.data_selector = 0; // We don't have this. 258 out->flt_save.mx_csr = info.fpregs.mxcsr; 259 out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; 260 memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); 261 memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); 262} 263 264static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, 265 const struct _libc_fpstate* fpregs) { 266 const greg_t* regs = uc->uc_mcontext.gregs; 267 268 out->context_flags = MD_CONTEXT_AMD64_FULL; 269 270 out->cs = regs[REG_CSGSFS] & 0xffff; 271 272 out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; 273 out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; 274 275 out->eflags = regs[REG_EFL]; 276 277 out->rax = regs[REG_RAX]; 278 out->rcx = regs[REG_RCX]; 279 out->rdx = regs[REG_RDX]; 280 out->rbx = regs[REG_RBX]; 281 282 out->rsp = regs[REG_RSP]; 283 out->rbp = regs[REG_RBP]; 284 out->rsi = regs[REG_RSI]; 285 out->rdi = regs[REG_RDI]; 286 out->r8 = regs[REG_R8]; 287 out->r9 = regs[REG_R9]; 288 out->r10 = regs[REG_R10]; 289 out->r11 = regs[REG_R11]; 290 out->r12 = regs[REG_R12]; 291 out->r13 = regs[REG_R13]; 292 out->r14 = regs[REG_R14]; 293 out->r15 = regs[REG_R15]; 294 295 out->rip = regs[REG_RIP]; 296 297 out->flt_save.control_word = fpregs->cwd; 298 out->flt_save.status_word = fpregs->swd; 299 out->flt_save.tag_word = fpregs->ftw; 300 out->flt_save.error_opcode = fpregs->fop; 301 out->flt_save.error_offset = fpregs->rip; 302 out->flt_save.data_offset = fpregs->rdp; 303 out->flt_save.error_selector = 0; // We don't have this. 304 out->flt_save.data_selector = 0; // We don't have this. 305 out->flt_save.mx_csr = fpregs->mxcsr; 306 out->flt_save.mx_csr_mask = fpregs->mxcr_mask; 307 memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); 308 memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); 309} 310 311#elif defined(__ARMEL__) 312typedef MDRawContextARM RawContextCPU; 313 314static void CPUFillFromThreadInfo(MDRawContextARM *out, 315 const google_breakpad::ThreadInfo &info) { 316 out->context_flags = MD_CONTEXT_ARM_FULL; 317 318 for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) 319 out->iregs[i] = info.regs.uregs[i]; 320 // No CPSR register in ThreadInfo(it's not accessible via ptrace) 321 out->cpsr = 0; 322#if !defined(__ANDROID__) 323 out->float_save.fpscr = info.fpregs.fpsr | 324 (static_cast<u_int64_t>(info.fpregs.fpcr) << 32); 325 //TODO: sort this out, actually collect floating point registers 326 memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); 327 memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); 328#endif 329} 330 331static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc, 332 const struct _libc_fpstate* fpregs) { 333 out->context_flags = MD_CONTEXT_ARM_FULL; 334 335 out->iregs[0] = uc->uc_mcontext.arm_r0; 336 out->iregs[1] = uc->uc_mcontext.arm_r1; 337 out->iregs[2] = uc->uc_mcontext.arm_r2; 338 out->iregs[3] = uc->uc_mcontext.arm_r3; 339 out->iregs[4] = uc->uc_mcontext.arm_r4; 340 out->iregs[5] = uc->uc_mcontext.arm_r5; 341 out->iregs[6] = uc->uc_mcontext.arm_r6; 342 out->iregs[7] = uc->uc_mcontext.arm_r7; 343 out->iregs[8] = uc->uc_mcontext.arm_r8; 344 out->iregs[9] = uc->uc_mcontext.arm_r9; 345 out->iregs[10] = uc->uc_mcontext.arm_r10; 346 347 out->iregs[11] = uc->uc_mcontext.arm_fp; 348 out->iregs[12] = uc->uc_mcontext.arm_ip; 349 out->iregs[13] = uc->uc_mcontext.arm_sp; 350 out->iregs[14] = uc->uc_mcontext.arm_lr; 351 out->iregs[15] = uc->uc_mcontext.arm_pc; 352 353 out->cpsr = uc->uc_mcontext.arm_cpsr; 354 355 //TODO: fix this after fixing ExceptionHandler 356 out->float_save.fpscr = 0; 357 memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); 358 memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); 359} 360 361#else 362#error "This code has not been ported to your platform yet." 363#endif 364 365namespace google_breakpad { 366 367class MinidumpWriter { 368 public: 369 MinidumpWriter(const char* filename, 370 pid_t crashing_pid, 371 const ExceptionHandler::CrashContext* context) 372 : filename_(filename), 373 siginfo_(&context->siginfo), 374 ucontext_(&context->context), 375#if !defined(__ARM_EABI__) 376 float_state_(&context->float_state), 377#else 378 //TODO: fix this after fixing ExceptionHandler 379 float_state_(NULL), 380#endif 381 crashing_tid_(context->tid), 382 dumper_(crashing_pid), 383 memory_blocks_(dumper_.allocator()) { 384 } 385 386 bool Init() { 387 return dumper_.Init() && minidump_writer_.Open(filename_) && 388 dumper_.ThreadsSuspend(); 389 } 390 391 ~MinidumpWriter() { 392 minidump_writer_.Close(); 393 dumper_.ThreadsResume(); 394 } 395 396 bool Dump() { 397 // The dynamic linker makes information available that helps gdb find all 398 // DSOs loaded into the program. If we can access this information, we dump 399 // it to a MD_LINUX_DSO_DEBUG stream. 400 struct r_debug* r_debug = NULL; 401 uint32_t dynamic_length = 0; 402#if !defined(__ANDROID__) 403 // The Android NDK is missing structure definitions for most of this. 404 // For now, it's simpler just to skip it. 405 for (int i = 0;;) { 406 ElfW(Dyn) dyn; 407 dynamic_length += sizeof(dyn); 408 dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn)); 409 if (dyn.d_tag == DT_DEBUG) { 410 r_debug = (struct r_debug*)dyn.d_un.d_ptr; 411 continue; 412 } else if (dyn.d_tag == DT_NULL) 413 break; 414 } 415#endif 416 417 // A minidump file contains a number of tagged streams. This is the number 418 // of stream which we write. 419 unsigned kNumWriters = 12; 420 if (r_debug) 421 ++kNumWriters; 422 423 TypedMDRVA<MDRawHeader> header(&minidump_writer_); 424 TypedMDRVA<MDRawDirectory> dir(&minidump_writer_); 425 if (!header.Allocate()) 426 return false; 427 if (!dir.AllocateArray(kNumWriters)) 428 return false; 429 memset(header.get(), 0, sizeof(MDRawHeader)); 430 431 header.get()->signature = MD_HEADER_SIGNATURE; 432 header.get()->version = MD_HEADER_VERSION; 433 header.get()->time_date_stamp = time(NULL); 434 header.get()->stream_count = kNumWriters; 435 header.get()->stream_directory_rva = dir.position(); 436 437 unsigned dir_index = 0; 438 MDRawDirectory dirent; 439 440 if (!WriteThreadListStream(&dirent)) 441 return false; 442 dir.CopyIndex(dir_index++, &dirent); 443 444 if (!WriteMappings(&dirent)) 445 return false; 446 dir.CopyIndex(dir_index++, &dirent); 447 448 if (!WriteMemoryListStream(&dirent)) 449 return false; 450 dir.CopyIndex(dir_index++, &dirent); 451 452 if (!WriteExceptionStream(&dirent)) 453 return false; 454 dir.CopyIndex(dir_index++, &dirent); 455 456 if (!WriteSystemInfoStream(&dirent)) 457 return false; 458 dir.CopyIndex(dir_index++, &dirent); 459 460 dirent.stream_type = MD_LINUX_CPU_INFO; 461 if (!WriteFile(&dirent.location, "/proc/cpuinfo")) 462 NullifyDirectoryEntry(&dirent); 463 dir.CopyIndex(dir_index++, &dirent); 464 465 dirent.stream_type = MD_LINUX_PROC_STATUS; 466 if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) 467 NullifyDirectoryEntry(&dirent); 468 dir.CopyIndex(dir_index++, &dirent); 469 470 dirent.stream_type = MD_LINUX_LSB_RELEASE; 471 if (!WriteFile(&dirent.location, "/etc/lsb-release")) 472 NullifyDirectoryEntry(&dirent); 473 dir.CopyIndex(dir_index++, &dirent); 474 475 dirent.stream_type = MD_LINUX_CMD_LINE; 476 if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) 477 NullifyDirectoryEntry(&dirent); 478 dir.CopyIndex(dir_index++, &dirent); 479 480 dirent.stream_type = MD_LINUX_ENVIRON; 481 if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) 482 NullifyDirectoryEntry(&dirent); 483 dir.CopyIndex(dir_index++, &dirent); 484 485 dirent.stream_type = MD_LINUX_AUXV; 486 if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) 487 NullifyDirectoryEntry(&dirent); 488 dir.CopyIndex(dir_index++, &dirent); 489 490 dirent.stream_type = MD_LINUX_MAPS; 491 if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) 492 NullifyDirectoryEntry(&dirent); 493 dir.CopyIndex(dir_index++, &dirent); 494 495 if (r_debug) { 496 dirent.stream_type = MD_LINUX_DSO_DEBUG; 497 if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length)) 498 NullifyDirectoryEntry(&dirent); 499 dir.CopyIndex(dir_index++, &dirent); 500 } 501 502 // If you add more directory entries, don't forget to update kNumWriters, 503 // above. 504 505 dumper_.ThreadsResume(); 506 return true; 507 } 508 509 // Check if the top of the stack is part of a system call that has been 510 // redirected by the seccomp sandbox. If so, try to pop the stack frames 511 // all the way back to the point where the interception happened. 512 void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread, 513 uint8_t* stack_copy) { 514#if defined(__x86_64) 515 u_int64_t bp = cpu->rbp; 516 u_int64_t top = thread.stack.start_of_memory_range; 517 for (int i = 4; i--; ) { 518 if (bp < top || 519 bp + sizeof(bp) > thread.stack.start_of_memory_range + 520 thread.stack.memory.data_size || 521 bp & 1) { 522 break; 523 } 524 uint64_t old_top = top; 525 top = bp; 526 u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range; 527 memcpy(&bp, bp_addr, sizeof(bp)); 528 if (bp == 0xDEADBEEFDEADBEEFull) { 529 struct { 530 uint64_t r15; 531 uint64_t r14; 532 uint64_t r13; 533 uint64_t r12; 534 uint64_t r11; 535 uint64_t r10; 536 uint64_t r9; 537 uint64_t r8; 538 uint64_t rdi; 539 uint64_t rsi; 540 uint64_t rdx; 541 uint64_t rcx; 542 uint64_t rbx; 543 uint64_t deadbeef; 544 uint64_t rbp; 545 uint64_t fakeret; 546 uint64_t ret; 547 /* char redzone[128]; */ 548 } seccomp_stackframe; 549 if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top || 550 top - offsetof(typeof(seccomp_stackframe), deadbeef) + 551 sizeof(seccomp_stackframe) > 552 thread.stack.start_of_memory_range+thread.stack.memory.data_size) { 553 break; 554 } 555 memcpy(&seccomp_stackframe, 556 bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef), 557 sizeof(seccomp_stackframe)); 558 cpu->rbx = seccomp_stackframe.rbx; 559 cpu->rcx = seccomp_stackframe.rcx; 560 cpu->rdx = seccomp_stackframe.rdx; 561 cpu->rsi = seccomp_stackframe.rsi; 562 cpu->rdi = seccomp_stackframe.rdi; 563 cpu->rbp = seccomp_stackframe.rbp; 564 cpu->rsp = top + 4*sizeof(uint64_t) + 128; 565 cpu->r8 = seccomp_stackframe.r8; 566 cpu->r9 = seccomp_stackframe.r9; 567 cpu->r10 = seccomp_stackframe.r10; 568 cpu->r11 = seccomp_stackframe.r11; 569 cpu->r12 = seccomp_stackframe.r12; 570 cpu->r13 = seccomp_stackframe.r13; 571 cpu->r14 = seccomp_stackframe.r14; 572 cpu->r15 = seccomp_stackframe.r15; 573 cpu->rip = seccomp_stackframe.fakeret; 574 return; 575 } 576 } 577#elif defined(__i386) 578 u_int32_t bp = cpu->ebp; 579 u_int32_t top = thread.stack.start_of_memory_range; 580 for (int i = 4; i--; ) { 581 if (bp < top || 582 bp + sizeof(bp) > thread.stack.start_of_memory_range + 583 thread.stack.memory.data_size || 584 bp & 1) { 585 break; 586 } 587 uint32_t old_top = top; 588 top = bp; 589 u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range; 590 memcpy(&bp, bp_addr, sizeof(bp)); 591 if (bp == 0xDEADBEEFu) { 592 struct { 593 uint32_t edi; 594 uint32_t esi; 595 uint32_t edx; 596 uint32_t ecx; 597 uint32_t ebx; 598 uint32_t deadbeef; 599 uint32_t ebp; 600 uint32_t fakeret; 601 uint32_t ret; 602 } seccomp_stackframe; 603 if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top || 604 top - offsetof(typeof(seccomp_stackframe), deadbeef) + 605 sizeof(seccomp_stackframe) > 606 thread.stack.start_of_memory_range+thread.stack.memory.data_size) { 607 break; 608 } 609 memcpy(&seccomp_stackframe, 610 bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef), 611 sizeof(seccomp_stackframe)); 612 cpu->ebx = seccomp_stackframe.ebx; 613 cpu->ecx = seccomp_stackframe.ecx; 614 cpu->edx = seccomp_stackframe.edx; 615 cpu->esi = seccomp_stackframe.esi; 616 cpu->edi = seccomp_stackframe.edi; 617 cpu->ebp = seccomp_stackframe.ebp; 618 cpu->esp = top + 4*sizeof(void*); 619 cpu->eip = seccomp_stackframe.fakeret; 620 return; 621 } 622 } 623#endif 624 } 625 626 // Write information about the threads. 627 bool WriteThreadListStream(MDRawDirectory* dirent) { 628 const unsigned num_threads = dumper_.threads().size(); 629 630 TypedMDRVA<uint32_t> list(&minidump_writer_); 631 if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) 632 return false; 633 634 dirent->stream_type = MD_THREAD_LIST_STREAM; 635 dirent->location = list.location(); 636 637 *list.get() = num_threads; 638 639 for (unsigned i = 0; i < num_threads; ++i) { 640 MDRawThread thread; 641 my_memset(&thread, 0, sizeof(thread)); 642 thread.thread_id = dumper_.threads()[i]; 643 // We have a different source of information for the crashing thread. If 644 // we used the actual state of the thread we would find it running in the 645 // signal handler with the alternative stack, which would be deeply 646 // unhelpful. 647 if ((pid_t)thread.thread_id == crashing_tid_) { 648 const void* stack; 649 size_t stack_len; 650 if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) 651 return false; 652 UntypedMDRVA memory(&minidump_writer_); 653 if (!memory.Allocate(stack_len)) 654 return false; 655 uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); 656 dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); 657 memory.Copy(stack_copy, stack_len); 658 thread.stack.start_of_memory_range = (uintptr_t) (stack); 659 thread.stack.memory = memory.location(); 660 memory_blocks_.push_back(thread.stack); 661 662 // Copy 256 bytes around crashing instruction pointer to minidump. 663 const size_t kIPMemorySize = 256; 664 u_int64_t ip = GetInstructionPointer(); 665 // Bound it to the upper and lower bounds of the memory map 666 // it's contained within. If it's not in mapped memory, 667 // don't bother trying to write it. 668 bool ip_is_mapped = false; 669 MDMemoryDescriptor ip_memory_d; 670 for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { 671 const MappingInfo& mapping = *dumper_.mappings()[i]; 672 if (ip >= mapping.start_addr && 673 ip < mapping.start_addr + mapping.size) { 674 ip_is_mapped = true; 675 // Try to get 128 bytes before and after the IP, but 676 // settle for whatever's available. 677 ip_memory_d.start_of_memory_range = 678 std::max(mapping.start_addr, 679 uintptr_t(ip - (kIPMemorySize / 2))); 680 uintptr_t end_of_range = 681 std::min(uintptr_t(ip + (kIPMemorySize / 2)), 682 uintptr_t(mapping.start_addr + mapping.size)); 683 ip_memory_d.memory.data_size = 684 end_of_range - ip_memory_d.start_of_memory_range; 685 break; 686 } 687 } 688 689 if (ip_is_mapped) { 690 UntypedMDRVA ip_memory(&minidump_writer_); 691 if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) 692 return false; 693 uint8_t* memory_copy = 694 (uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size); 695 dumper_.CopyFromProcess( 696 memory_copy, 697 thread.thread_id, 698 reinterpret_cast<void*>(ip_memory_d.start_of_memory_range), 699 ip_memory_d.memory.data_size); 700 ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size); 701 ip_memory_d.memory = ip_memory.location(); 702 memory_blocks_.push_back(ip_memory_d); 703 } 704 705 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); 706 if (!cpu.Allocate()) 707 return false; 708 my_memset(cpu.get(), 0, sizeof(RawContextCPU)); 709 CPUFillFromUContext(cpu.get(), ucontext_, float_state_); 710 PopSeccompStackFrame(cpu.get(), thread, stack_copy); 711 thread.thread_context = cpu.location(); 712 crashing_thread_context_ = cpu.location(); 713 } else { 714 ThreadInfo info; 715 if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) 716 return false; 717 UntypedMDRVA memory(&minidump_writer_); 718 if (!memory.Allocate(info.stack_len)) 719 return false; 720 uint8_t* stack_copy = 721 (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); 722 dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, 723 info.stack_len); 724 memory.Copy(stack_copy, info.stack_len); 725 thread.stack.start_of_memory_range = (uintptr_t)(info.stack); 726 thread.stack.memory = memory.location(); 727 memory_blocks_.push_back(thread.stack); 728 729 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); 730 if (!cpu.Allocate()) 731 return false; 732 my_memset(cpu.get(), 0, sizeof(RawContextCPU)); 733 CPUFillFromThreadInfo(cpu.get(), info); 734 PopSeccompStackFrame(cpu.get(), thread, stack_copy); 735 thread.thread_context = cpu.location(); 736 } 737 738 list.CopyIndexAfterObject(i, &thread, sizeof(thread)); 739 } 740 741 return true; 742 } 743 744 static bool ShouldIncludeMapping(const MappingInfo& mapping) { 745 if (mapping.name[0] == 0 || // we only want modules with filenames. 746 mapping.offset || // we only want to include one mapping per shared lib. 747 mapping.size < 4096) { // too small to get a signature for. 748 return false; 749 } 750 751 return true; 752 } 753 754 // Write information about the mappings in effect. Because we are using the 755 // minidump format, the information about the mappings is pretty limited. 756 // Because of this, we also include the full, unparsed, /proc/$x/maps file in 757 // another stream in the file. 758 bool WriteMappings(MDRawDirectory* dirent) { 759 const unsigned num_mappings = dumper_.mappings().size(); 760 unsigned num_output_mappings = 0; 761 762 for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { 763 const MappingInfo& mapping = *dumper_.mappings()[i]; 764 if (ShouldIncludeMapping(mapping)) 765 num_output_mappings++; 766 } 767 768 TypedMDRVA<uint32_t> list(&minidump_writer_); 769 if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) 770 return false; 771 772 dirent->stream_type = MD_MODULE_LIST_STREAM; 773 dirent->location = list.location(); 774 *list.get() = num_output_mappings; 775 776 for (unsigned i = 0, j = 0; i < num_mappings; ++i) { 777 const MappingInfo& mapping = *dumper_.mappings()[i]; 778 if (!ShouldIncludeMapping(mapping)) 779 continue; 780 781 MDRawModule mod; 782 my_memset(&mod, 0, MD_MODULE_SIZE); 783 mod.base_of_image = mapping.start_addr; 784 mod.size_of_image = mapping.size; 785 const size_t filepath_len = my_strlen(mapping.name); 786 787 // Figure out file name from path 788 const char* filename_ptr = mapping.name + filepath_len - 1; 789 while (filename_ptr >= mapping.name) { 790 if (*filename_ptr == '/') 791 break; 792 filename_ptr--; 793 } 794 filename_ptr++; 795 const size_t filename_len = mapping.name + filepath_len - filename_ptr; 796 797 uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; 798 uint8_t* cv_ptr = cv_buf; 799 UntypedMDRVA cv(&minidump_writer_); 800 if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) 801 return false; 802 803 const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; 804 memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); 805 cv_ptr += sizeof(cv_signature); 806 uint8_t* signature = cv_ptr; 807 cv_ptr += sizeof(MDGUID); 808 dumper_.ElfFileIdentifierForMapping(i, signature); 809 my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. 810 cv_ptr += sizeof(uint32_t); 811 812 // Write pdb_file_name 813 memcpy(cv_ptr, filename_ptr, filename_len + 1); 814 cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); 815 816 mod.cv_record = cv.location(); 817 818 MDLocationDescriptor ld; 819 if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) 820 return false; 821 mod.module_name_rva = ld.rva; 822 823 list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); 824 } 825 826 return true; 827 } 828 829 bool WriteMemoryListStream(MDRawDirectory* dirent) { 830 TypedMDRVA<uint32_t> list(&minidump_writer_); 831 if (!list.AllocateObjectAndArray(memory_blocks_.size(), 832 sizeof(MDMemoryDescriptor))) 833 return false; 834 835 dirent->stream_type = MD_MEMORY_LIST_STREAM; 836 dirent->location = list.location(); 837 838 *list.get() = memory_blocks_.size(); 839 840 for (size_t i = 0; i < memory_blocks_.size(); ++i) { 841 list.CopyIndexAfterObject(i, &memory_blocks_[i], 842 sizeof(MDMemoryDescriptor)); 843 } 844 return true; 845 } 846 847 bool WriteExceptionStream(MDRawDirectory* dirent) { 848 TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_); 849 if (!exc.Allocate()) 850 return false; 851 my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); 852 853 dirent->stream_type = MD_EXCEPTION_STREAM; 854 dirent->location = exc.location(); 855 856 exc.get()->thread_id = crashing_tid_; 857 exc.get()->exception_record.exception_code = siginfo_->si_signo; 858 exc.get()->exception_record.exception_address = 859 (uintptr_t) siginfo_->si_addr; 860 exc.get()->thread_context = crashing_thread_context_; 861 862 return true; 863 } 864 865 bool WriteSystemInfoStream(MDRawDirectory* dirent) { 866 TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_); 867 if (!si.Allocate()) 868 return false; 869 my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); 870 871 dirent->stream_type = MD_SYSTEM_INFO_STREAM; 872 dirent->location = si.location(); 873 874 WriteCPUInformation(si.get()); 875 WriteOSInformation(si.get()); 876 877 return true; 878 } 879 880 bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug, 881 uint32_t dynamic_length) { 882#if defined(__ANDROID__) 883 return false; 884#else 885 // The caller provided us with a pointer to "struct r_debug". We can 886 // look up the "r_map" field to get a linked list of all loaded DSOs. 887 // Our list of DSOs potentially is different from the ones in the crashing 888 // process. So, we have to be careful to never dereference pointers 889 // directly. Instead, we use CopyFromProcess() everywhere. 890 // See <link.h> for a more detailed discussion of the how the dynamic 891 // loader communicates with debuggers. 892 893 // Count the number of loaded DSOs 894 int dso_count = 0; 895 struct r_debug debug_entry; 896 dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug, 897 sizeof(debug_entry)); 898 for (struct link_map* ptr = debug_entry.r_map; ptr; ) { 899 struct link_map map; 900 dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map)); 901 ptr = map.l_next; 902 dso_count++; 903 } 904 905 MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA; 906 if (dso_count > 0) { 907 // If we have at least one DSO, create an array of MDRawLinkMap 908 // entries in the minidump file. 909 TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_); 910 if (!linkmap.AllocateArray(dso_count)) 911 return false; 912 linkmap_rva = linkmap.location().rva; 913 int idx = 0; 914 915 // Iterate over DSOs and write their information to mini dump 916 for (struct link_map* ptr = debug_entry.r_map; ptr; ) { 917 struct link_map map; 918 dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map)); 919 ptr = map.l_next; 920 char filename[257] = { 0 }; 921 if (map.l_name) { 922 dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name, 923 sizeof(filename) - 1); 924 } 925 MDLocationDescriptor location; 926 if (!minidump_writer_.WriteString(filename, 0, &location)) 927 return false; 928 MDRawLinkMap entry; 929 entry.name = location.rva; 930 entry.addr = (void*)map.l_addr; 931 entry.ld = (void*)map.l_ld; 932 linkmap.CopyIndex(idx++, &entry); 933 } 934 } 935 936 // Write MD_LINUX_DSO_DEBUG record 937 TypedMDRVA<MDRawDebug> debug(&minidump_writer_); 938 if (!debug.AllocateObjectAndArray(1, dynamic_length)) 939 return false; 940 my_memset(debug.get(), 0, sizeof(MDRawDebug)); 941 dirent->stream_type = MD_LINUX_DSO_DEBUG; 942 dirent->location = debug.location(); 943 944 debug.get()->version = debug_entry.r_version; 945 debug.get()->map = linkmap_rva; 946 debug.get()->dso_count = dso_count; 947 debug.get()->brk = (void*)debug_entry.r_brk; 948 debug.get()->ldbase = (void*)debug_entry.r_ldbase; 949 debug.get()->dynamic = (void*)&_DYNAMIC; 950 951 char *dso_debug_data = new char[dynamic_length]; 952 dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC, 953 dynamic_length); 954 debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length); 955 delete[] dso_debug_data; 956 957 return true; 958#endif 959 } 960 961 private: 962#if defined(__i386) 963 uintptr_t GetStackPointer() { 964 return ucontext_->uc_mcontext.gregs[REG_ESP]; 965 } 966 967 uintptr_t GetInstructionPointer() { 968 return ucontext_->uc_mcontext.gregs[REG_EIP]; 969 } 970#elif defined(__x86_64) 971 uintptr_t GetStackPointer() { 972 return ucontext_->uc_mcontext.gregs[REG_RSP]; 973 } 974 975 uintptr_t GetInstructionPointer() { 976 return ucontext_->uc_mcontext.gregs[REG_RIP]; 977 } 978#elif defined(__ARM_EABI__) 979 uintptr_t GetStackPointer() { 980 return ucontext_->uc_mcontext.arm_sp; 981 } 982 983 uintptr_t GetInstructionPointer() { 984 return ucontext_->uc_mcontext.arm_ip; 985 } 986#else 987#error "This code has not been ported to your platform yet." 988#endif 989 990 void NullifyDirectoryEntry(MDRawDirectory* dirent) { 991 dirent->stream_type = 0; 992 dirent->location.data_size = 0; 993 dirent->location.rva = 0; 994 } 995 996 bool WriteCPUInformation(MDRawSystemInfo* sys_info) { 997 char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; 998 static const char vendor_id_name[] = "vendor_id"; 999 static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; 1000 1001 struct CpuInfoEntry { 1002 const char* info_name; 1003 int value; 1004 bool found; 1005 } cpu_info_table[] = { 1006 { "processor", -1, false }, 1007 { "model", 0, false }, 1008 { "stepping", 0, false }, 1009 { "cpu family", 0, false }, 1010 }; 1011 1012 // processor_architecture should always be set, do this first 1013 sys_info->processor_architecture = 1014#if defined(__i386) 1015 MD_CPU_ARCHITECTURE_X86; 1016#elif defined(__x86_64) 1017 MD_CPU_ARCHITECTURE_AMD64; 1018#elif defined(__arm__) 1019 MD_CPU_ARCHITECTURE_ARM; 1020#else 1021#error "Unknown CPU arch" 1022#endif 1023 1024 const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); 1025 if (fd < 0) 1026 return false; 1027 1028 { 1029 PageAllocator allocator; 1030 LineReader* const line_reader = new(allocator) LineReader(fd); 1031 const char* line; 1032 unsigned line_len; 1033 while (line_reader->GetNextLine(&line, &line_len)) { 1034 for (size_t i = 0; 1035 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); 1036 i++) { 1037 CpuInfoEntry* entry = &cpu_info_table[i]; 1038 if (entry->found && i) 1039 continue; 1040 if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { 1041 const char* value = strchr(line, ':'); 1042 if (!value) 1043 continue; 1044 1045 // the above strncmp only matches the prefix, it might be the wrong 1046 // line. i.e. we matched "model name" instead of "model". 1047 // check and make sure there is only spaces between the prefix and 1048 // the colon. 1049 const char* space_ptr = line + strlen(entry->info_name); 1050 for (; space_ptr < value; space_ptr++) { 1051 if (!isspace(*space_ptr)) { 1052 break; 1053 } 1054 } 1055 if (space_ptr != value) 1056 continue; 1057 1058 sscanf(++value, " %d", &(entry->value)); 1059 entry->found = true; 1060 } 1061 } 1062 1063 // special case for vendor_id 1064 if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { 1065 const char* value = strchr(line, ':'); 1066 if (!value) 1067 goto popline; 1068 1069 // skip ':" and all the spaces that follows 1070 do { 1071 value++; 1072 } while (isspace(*value)); 1073 1074 if (*value) { 1075 size_t length = strlen(value); 1076 if (length == 0) 1077 goto popline; 1078 // we don't want the trailing newline 1079 if (value[length - 1] == '\n') 1080 length--; 1081 // ensure we have space for the value 1082 if (length < sizeof(vendor_id)) 1083 strncpy(vendor_id, value, length); 1084 } 1085 } 1086 1087popline: 1088 line_reader->PopLine(line_len); 1089 } 1090 sys_close(fd); 1091 } 1092 1093 // make sure we got everything we wanted 1094 for (size_t i = 0; 1095 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); 1096 i++) { 1097 if (!cpu_info_table[i].found) { 1098 return false; 1099 } 1100 } 1101 // /proc/cpuinfo contains cpu id, change it into number by adding one. 1102 cpu_info_table[0].value++; 1103 1104 sys_info->number_of_processors = cpu_info_table[0].value; 1105 sys_info->processor_level = cpu_info_table[3].value; 1106 sys_info->processor_revision = cpu_info_table[1].value << 8 | 1107 cpu_info_table[2].value; 1108 1109 if (vendor_id[0] != '\0') { 1110 memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, 1111 sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); 1112 } 1113 return true; 1114 } 1115 1116 bool WriteFile(MDLocationDescriptor* result, const char* filename) { 1117 const int fd = sys_open(filename, O_RDONLY, 0); 1118 if (fd < 0) 1119 return false; 1120 1121 // We can't stat the files because several of the files that we want to 1122 // read are kernel seqfiles, which always have a length of zero. So we have 1123 // to read as much as we can into a buffer. 1124 static const unsigned kBufSize = 1024 - 2*sizeof(void*); 1125 struct Buffers { 1126 struct Buffers* next; 1127 size_t len; 1128 uint8_t data[kBufSize]; 1129 } *buffers = 1130 (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers)); 1131 buffers->next = NULL; 1132 buffers->len = 0; 1133 1134 size_t total = 0; 1135 for (struct Buffers* bufptr = buffers;;) { 1136 ssize_t r; 1137 do { 1138 r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); 1139 } while (r == -1 && errno == EINTR); 1140 1141 if (r < 1) 1142 break; 1143 1144 total += r; 1145 bufptr->len += r; 1146 if (bufptr->len == kBufSize) { 1147 bufptr->next = 1148 (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers)); 1149 bufptr = bufptr->next; 1150 bufptr->next = NULL; 1151 bufptr->len = 0; 1152 } 1153 } 1154 sys_close(fd); 1155 1156 if (!total) 1157 return false; 1158 1159 UntypedMDRVA memory(&minidump_writer_); 1160 if (!memory.Allocate(total)) 1161 return false; 1162 for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) { 1163 memory.Copy(pos, &buffers->data, buffers->len); 1164 pos += buffers->len; 1165 } 1166 *result = memory.location(); 1167 return true; 1168 } 1169 1170 bool WriteOSInformation(MDRawSystemInfo* sys_info) { 1171 sys_info->platform_id = MD_OS_LINUX; 1172 1173 struct utsname uts; 1174 if (uname(&uts)) 1175 return false; 1176 1177 static const size_t buf_len = 512; 1178 char buf[buf_len] = {0}; 1179 size_t space_left = buf_len - 1; 1180 const char* info_table[] = { 1181 uts.sysname, 1182 uts.release, 1183 uts.version, 1184 uts.machine, 1185 NULL 1186 }; 1187 bool first_item = true; 1188 for (const char** cur_info = info_table; *cur_info; cur_info++) { 1189 static const char* separator = " "; 1190 size_t separator_len = strlen(separator); 1191 size_t info_len = strlen(*cur_info); 1192 if (info_len == 0) 1193 continue; 1194 1195 if (space_left < info_len + (first_item ? 0 : separator_len)) 1196 break; 1197 1198 if (!first_item) { 1199 strcat(buf, separator); 1200 space_left -= separator_len; 1201 } 1202 1203 first_item = false; 1204 strcat(buf, *cur_info); 1205 space_left -= info_len; 1206 } 1207 1208 MDLocationDescriptor location; 1209 if (!minidump_writer_.WriteString(buf, 0, &location)) 1210 return false; 1211 sys_info->csd_version_rva = location.rva; 1212 1213 return true; 1214 } 1215 1216 bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, 1217 const char* filename) { 1218 char buf[80]; 1219 memcpy(buf, "/proc/", 6); 1220 const unsigned pid_len = my_int_len(pid); 1221 my_itos(buf + 6, pid, pid_len); 1222 buf[6 + pid_len] = '/'; 1223 memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1); 1224 return WriteFile(result, buf); 1225 } 1226 1227 const char* const filename_; // output filename 1228 const siginfo_t* const siginfo_; // from the signal handler (see sigaction) 1229 const struct ucontext* const ucontext_; // also from the signal handler 1230 const struct _libc_fpstate* const float_state_; // ditto 1231 const pid_t crashing_tid_; // the process which actually crashed 1232 LinuxDumper dumper_; 1233 MinidumpFileWriter minidump_writer_; 1234 MDLocationDescriptor crashing_thread_context_; 1235 // Blocks of memory written to the dump. These are all currently 1236 // written while writing the thread list stream, but saved here 1237 // so a memory list stream can be written afterwards. 1238 wasteful_vector<MDMemoryDescriptor> memory_blocks_; 1239}; 1240 1241bool WriteMinidump(const char* filename, pid_t crashing_process, 1242 const void* blob, size_t blob_size) { 1243 if (blob_size != sizeof(ExceptionHandler::CrashContext)) 1244 return false; 1245 const ExceptionHandler::CrashContext* context = 1246 reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); 1247 MinidumpWriter writer(filename, crashing_process, context); 1248 if (!writer.Init()) 1249 return false; 1250 return writer.Dump(); 1251} 1252 1253} // namespace google_breakpad 1254