Home | History | Annotate | Download | only in minidump_writer
      1 // Copyright (c) 2012, 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 // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
     31 // See linux_core_dumper.h for details.
     32 
     33 #include "client/linux/minidump_writer/linux_core_dumper.h"
     34 
     35 #include <asm/ptrace.h>
     36 #include <assert.h>
     37 #include <elf.h>
     38 #include <stdio.h>
     39 #include <string.h>
     40 #include <sys/procfs.h>
     41 
     42 #include "common/linux/linux_libc_support.h"
     43 
     44 namespace google_breakpad {
     45 
     46 LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
     47                                  const char* core_path,
     48                                  const char* procfs_path)
     49     : LinuxDumper(pid),
     50       core_path_(core_path),
     51       procfs_path_(procfs_path),
     52       thread_infos_(&allocator_, 8) {
     53   assert(core_path_);
     54 }
     55 
     56 bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
     57                                     const char* node) const {
     58   if (!path || !node)
     59     return false;
     60 
     61   size_t node_len = my_strlen(node);
     62   if (node_len == 0)
     63     return false;
     64 
     65   size_t procfs_path_len = my_strlen(procfs_path_);
     66   size_t total_length = procfs_path_len + 1 + node_len;
     67   if (total_length >= NAME_MAX)
     68     return false;
     69 
     70   memcpy(path, procfs_path_, procfs_path_len);
     71   path[procfs_path_len] = '/';
     72   memcpy(path + procfs_path_len + 1, node, node_len);
     73   path[total_length] = '\0';
     74   return true;
     75 }
     76 
     77 bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
     78                                       const void* src, size_t length) {
     79   ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
     80   // TODO(benchan): Investigate whether the data to be copied could span
     81   // across multiple segments in the core dump file. ElfCoreDump::CopyData
     82   // and this method do not handle that case yet.
     83   if (!core_.CopyData(dest, virtual_address, length)) {
     84     // If the data segment is not found in the core dump, fill the result
     85     // with marker characters.
     86     memset(dest, 0xab, length);
     87     return false;
     88   }
     89   return true;
     90 }
     91 
     92 bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
     93   if (index >= thread_infos_.size())
     94     return false;
     95 
     96   *info = thread_infos_[index];
     97   const uint8_t* stack_pointer;
     98 #if defined(__i386)
     99   memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
    100 #elif defined(__x86_64)
    101   memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
    102 #elif defined(__ARM_EABI__)
    103   memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
    104 #elif defined(__aarch64__)
    105   memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
    106 #elif defined(__mips__)
    107   stack_pointer =
    108       reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
    109 #else
    110 #error "This code hasn't been ported to your platform yet."
    111 #endif
    112   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
    113   return true;
    114 }
    115 
    116 bool LinuxCoreDumper::IsPostMortem() const {
    117   return true;
    118 }
    119 
    120 bool LinuxCoreDumper::ThreadsSuspend() {
    121   return true;
    122 }
    123 
    124 bool LinuxCoreDumper::ThreadsResume() {
    125   return true;
    126 }
    127 
    128 bool LinuxCoreDumper::EnumerateThreads() {
    129   if (!mapped_core_file_.Map(core_path_, 0)) {
    130     fprintf(stderr, "Could not map core dump file into memory\n");
    131     return false;
    132   }
    133 
    134   core_.SetContent(mapped_core_file_.content());
    135   if (!core_.IsValid()) {
    136     fprintf(stderr, "Invalid core dump file\n");
    137     return false;
    138   }
    139 
    140   ElfCoreDump::Note note = core_.GetFirstNote();
    141   if (!note.IsValid()) {
    142     fprintf(stderr, "PT_NOTE section not found\n");
    143     return false;
    144   }
    145 
    146   bool first_thread = true;
    147   do {
    148     ElfCoreDump::Word type = note.GetType();
    149     MemoryRange name = note.GetName();
    150     MemoryRange description = note.GetDescription();
    151 
    152     if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
    153       fprintf(stderr, "Could not found a valid PT_NOTE.\n");
    154       return false;
    155     }
    156 
    157     // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
    158     // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
    159     //   Thread           Name          Type
    160     //   -------------------------------------------------------------------
    161     //   1st thread       CORE          NT_PRSTATUS
    162     //   process-wide     CORE          NT_PRPSINFO
    163     //   process-wide     CORE          NT_AUXV
    164     //   1st thread       CORE          NT_FPREGSET
    165     //   1st thread       LINUX         NT_PRXFPREG
    166     //   1st thread       LINUX         NT_386_TLS
    167     //
    168     //   2nd thread       CORE          NT_PRSTATUS
    169     //   2nd thread       CORE          NT_FPREGSET
    170     //   2nd thread       LINUX         NT_PRXFPREG
    171     //   2nd thread       LINUX         NT_386_TLS
    172     //
    173     //   3rd thread       CORE          NT_PRSTATUS
    174     //   3rd thread       CORE          NT_FPREGSET
    175     //   3rd thread       LINUX         NT_PRXFPREG
    176     //   3rd thread       LINUX         NT_386_TLS
    177     //
    178     // The following code only works if notes are ordered as expected.
    179     switch (type) {
    180       case NT_PRSTATUS: {
    181         if (description.length() != sizeof(elf_prstatus)) {
    182           fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
    183           return false;
    184         }
    185 
    186         const elf_prstatus* status =
    187             reinterpret_cast<const elf_prstatus*>(description.data());
    188         pid_t pid = status->pr_pid;
    189         ThreadInfo info;
    190         memset(&info, 0, sizeof(ThreadInfo));
    191         info.tgid = status->pr_pgrp;
    192         info.ppid = status->pr_ppid;
    193 #if defined(__mips__)
    194         for (int i = EF_REG0; i <= EF_REG31; i++)
    195           info.regs.regs[i - EF_REG0] = status->pr_reg[i];
    196 
    197         info.regs.lo = status->pr_reg[EF_LO];
    198         info.regs.hi = status->pr_reg[EF_HI];
    199         info.regs.epc = status->pr_reg[EF_CP0_EPC];
    200         info.regs.badvaddr = status->pr_reg[EF_CP0_BADVADDR];
    201         info.regs.status = status->pr_reg[EF_CP0_STATUS];
    202         info.regs.cause = status->pr_reg[EF_CP0_CAUSE];
    203 #else
    204         memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
    205 #endif
    206         if (first_thread) {
    207           crash_thread_ = pid;
    208           crash_signal_ = status->pr_info.si_signo;
    209         }
    210         first_thread = false;
    211         threads_.push_back(pid);
    212         thread_infos_.push_back(info);
    213         break;
    214       }
    215 #if defined(__i386) || defined(__x86_64)
    216       case NT_FPREGSET: {
    217         if (thread_infos_.empty())
    218           return false;
    219 
    220         ThreadInfo* info = &thread_infos_.back();
    221         if (description.length() != sizeof(info->fpregs)) {
    222           fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
    223           return false;
    224         }
    225 
    226         memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
    227         break;
    228       }
    229 #endif
    230 #if defined(__i386)
    231       case NT_PRXFPREG: {
    232         if (thread_infos_.empty())
    233           return false;
    234 
    235         ThreadInfo* info = &thread_infos_.back();
    236         if (description.length() != sizeof(info->fpxregs)) {
    237           fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
    238           return false;
    239         }
    240 
    241         memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
    242         break;
    243       }
    244 #endif
    245     }
    246     note = note.GetNextNote();
    247   } while (note.IsValid());
    248 
    249   return true;
    250 }
    251 
    252 }  // namespace google_breakpad
    253