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