linux_core_dumper.cc
1 // Copyright 2012 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. 30 // See linux_core_dumper.h for details. 31 32 #ifdef HAVE_CONFIG_H 33 #include <config.h> // Must come first 34 #endif 35 36 #include "client/linux/minidump_writer/linux_core_dumper.h" 37 38 #include <asm/ptrace.h> 39 #include <assert.h> 40 #include <elf.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <sys/procfs.h> 44 #if defined(__mips__) && defined(__ANDROID__) 45 // To get register definitions. 46 #include <asm/reg.h> 47 #endif 48 49 #include "common/linux/elf_gnu_compat.h" 50 #include "common/linux/linux_libc_support.h" 51 52 namespace google_breakpad { 53 54 LinuxCoreDumper::LinuxCoreDumper(pid_t pid, 55 const char* core_path, 56 const char* procfs_path, 57 const char* root_prefix) 58 : LinuxDumper(pid, root_prefix), 59 core_path_(core_path), 60 procfs_path_(procfs_path), 61 thread_infos_(&allocator_, 8) { 62 assert(core_path_); 63 } 64 65 bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, 66 const char* node) const { 67 if (!path || !node) 68 return false; 69 70 size_t node_len = my_strlen(node); 71 if (node_len == 0) 72 return false; 73 74 size_t procfs_path_len = my_strlen(procfs_path_); 75 size_t total_length = procfs_path_len + 1 + node_len; 76 if (total_length >= NAME_MAX) 77 return false; 78 79 memcpy(path, procfs_path_, procfs_path_len); 80 path[procfs_path_len] = '/'; 81 memcpy(path + procfs_path_len + 1, node, node_len); 82 path[total_length] = '\0'; 83 return true; 84 } 85 86 bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, 87 const void* src, size_t length) { 88 ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src); 89 // TODO(benchan): Investigate whether the data to be copied could span 90 // across multiple segments in the core dump file. ElfCoreDump::CopyData 91 // and this method do not handle that case yet. 92 if (!core_.CopyData(dest, virtual_address, length)) { 93 // If the data segment is not found in the core dump, fill the result 94 // with marker characters. 95 memset(dest, 0xab, length); 96 return false; 97 } 98 return true; 99 } 100 101 bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { 102 if (index >= thread_infos_.size()) 103 return false; 104 105 *info = thread_infos_[index]; 106 const uint8_t* stack_pointer; 107 #if defined(__i386) 108 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); 109 #elif defined(__x86_64) 110 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); 111 #elif defined(__ARM_EABI__) 112 memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); 113 #elif defined(__aarch64__) 114 memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); 115 #elif defined(__mips__) 116 stack_pointer = 117 reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); 118 #elif defined(__riscv) 119 stack_pointer = reinterpret_cast<uint8_t*>( 120 info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]); 121 #else 122 # error "This code hasn't been ported to your platform yet." 123 #endif 124 info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); 125 return true; 126 } 127 128 bool LinuxCoreDumper::IsPostMortem() const { 129 return true; 130 } 131 132 bool LinuxCoreDumper::ThreadsSuspend() { 133 return true; 134 } 135 136 bool LinuxCoreDumper::ThreadsResume() { 137 return true; 138 } 139 140 bool LinuxCoreDumper::EnumerateThreads() { 141 if (!mapped_core_file_.Map(core_path_, 0)) { 142 fprintf(stderr, "Could not map core dump file into memory\n"); 143 return false; 144 } 145 146 char proc_mem_path[NAME_MAX]; 147 if (BuildProcPath(proc_mem_path, pid_, "mem")) { 148 int fd = open(proc_mem_path, O_RDONLY | O_LARGEFILE | O_CLOEXEC); 149 if (fd != -1) { 150 core_.SetProcMem(fd); 151 } else { 152 fprintf(stderr, "Cannot open %s (%s)\n", proc_mem_path, strerror(errno)); 153 } 154 } 155 156 core_.SetContent(mapped_core_file_.content()); 157 if (!core_.IsValid()) { 158 fprintf(stderr, "Invalid core dump file\n"); 159 return false; 160 } 161 162 ElfCoreDump::Note note = core_.GetFirstNote(); 163 if (!note.IsValid()) { 164 fprintf(stderr, "PT_NOTE section not found\n"); 165 return false; 166 } 167 168 bool first_thread = true; 169 do { 170 ElfCoreDump::Word type = note.GetType(); 171 MemoryRange name = note.GetName(); 172 MemoryRange description = note.GetDescription(); 173 174 if (type == 0 || name.IsEmpty() || description.IsEmpty()) { 175 fprintf(stderr, "Could not found a valid PT_NOTE.\n"); 176 return false; 177 } 178 179 // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are 180 // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): 181 // Thread Name Type 182 // ------------------------------------------------------------------- 183 // 1st thread CORE NT_PRSTATUS 184 // process-wide CORE NT_PRPSINFO 185 // process-wide CORE NT_SIGINFO 186 // process-wide CORE NT_AUXV 187 // 1st thread CORE NT_FPREGSET 188 // 1st thread LINUX NT_PRXFPREG 189 // 1st thread LINUX NT_386_TLS 190 // 191 // 2nd thread CORE NT_PRSTATUS 192 // 2nd thread CORE NT_FPREGSET 193 // 2nd thread LINUX NT_PRXFPREG 194 // 2nd thread LINUX NT_386_TLS 195 // 196 // 3rd thread CORE NT_PRSTATUS 197 // 3rd thread CORE NT_FPREGSET 198 // 3rd thread LINUX NT_PRXFPREG 199 // 3rd thread LINUX NT_386_TLS 200 // 201 // The following code only works if notes are ordered as expected. 202 switch (type) { 203 case NT_PRSTATUS: { 204 if (description.length() != sizeof(elf_prstatus)) { 205 fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); 206 return false; 207 } 208 209 const elf_prstatus* status = 210 reinterpret_cast<const elf_prstatus*>(description.data()); 211 pid_t pid = status->pr_pid; 212 ThreadInfo info; 213 memset(&info, 0, sizeof(ThreadInfo)); 214 info.tgid = status->pr_pgrp; 215 info.ppid = status->pr_ppid; 216 #if defined(__mips__) 217 # if defined(__ANDROID__) 218 for (int i = EF_R0; i <= EF_R31; i++) 219 info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; 220 # else // __ANDROID__ 221 for (int i = EF_REG0; i <= EF_REG31; i++) 222 info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i]; 223 # endif // __ANDROID__ 224 info.mcontext.mdlo = status->pr_reg[EF_LO]; 225 info.mcontext.mdhi = status->pr_reg[EF_HI]; 226 info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; 227 #elif defined(__riscv) 228 memcpy(&info.mcontext.__gregs, status->pr_reg, 229 sizeof(info.mcontext.__gregs)); 230 #else // __riscv 231 memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); 232 #endif 233 if (first_thread) { 234 crash_thread_ = pid; 235 crash_signal_ = status->pr_info.si_signo; 236 crash_signal_code_ = status->pr_info.si_code; 237 } 238 first_thread = false; 239 threads_.push_back(pid); 240 thread_infos_.push_back(info); 241 break; 242 } 243 case NT_SIGINFO: { 244 if (description.length() != sizeof(siginfo_t)) { 245 fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n"); 246 return false; 247 } 248 249 const siginfo_t* info = 250 reinterpret_cast<const siginfo_t*>(description.data()); 251 252 // Set crash_address when si_addr is valid for the signal. 253 switch (info->si_signo) { 254 case MD_EXCEPTION_CODE_LIN_SIGBUS: 255 case MD_EXCEPTION_CODE_LIN_SIGFPE: 256 case MD_EXCEPTION_CODE_LIN_SIGILL: 257 case MD_EXCEPTION_CODE_LIN_SIGSEGV: 258 case MD_EXCEPTION_CODE_LIN_SIGSYS: 259 case MD_EXCEPTION_CODE_LIN_SIGTRAP: 260 crash_address_ = reinterpret_cast<uintptr_t>(info->si_addr); 261 break; 262 } 263 264 // Set crash_exception_info for common signals. Since exception info is 265 // unsigned, but some of these fields might be signed, we always cast. 266 switch (info->si_signo) { 267 case MD_EXCEPTION_CODE_LIN_SIGKILL: 268 set_crash_exception_info({ 269 static_cast<uint64_t>(info->si_pid), 270 static_cast<uint64_t>(info->si_uid), 271 }); 272 break; 273 case MD_EXCEPTION_CODE_LIN_SIGSYS: 274 #ifdef si_syscall 275 set_crash_exception_info({ 276 static_cast<uint64_t>(info->si_syscall), 277 static_cast<uint64_t>(info->si_arch), 278 }); 279 #endif 280 break; 281 } 282 break; 283 } 284 #if defined(__i386) || defined(__x86_64) 285 case NT_FPREGSET: { 286 if (thread_infos_.empty()) 287 return false; 288 289 ThreadInfo* info = &thread_infos_.back(); 290 if (description.length() != sizeof(info->fpregs)) { 291 fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); 292 return false; 293 } 294 295 memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); 296 break; 297 } 298 #endif 299 #if defined(__i386) 300 case NT_PRXFPREG: { 301 if (thread_infos_.empty()) 302 return false; 303 304 ThreadInfo* info = &thread_infos_.back(); 305 if (description.length() != sizeof(info->fpxregs)) { 306 fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); 307 return false; 308 } 309 310 memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); 311 break; 312 } 313 #endif 314 } 315 note = note.GetNextNote(); 316 } while (note.IsValid()); 317 318 return true; 319 } 320 321 } // namespace google_breakpad