• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(__mips__) && defined(__ANDROID__)
42 // To get register definitions.
43 #include <asm/reg.h>
44 #endif
45 
46 #include "common/linux/elf_gnu_compat.h"
47 #include "common/linux/linux_libc_support.h"
48 
49 namespace google_breakpad {
50 
LinuxCoreDumper(pid_t pid,const char * core_path,const char * procfs_path,const char * root_prefix)51 LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
52                                  const char* core_path,
53                                  const char* procfs_path,
54                                  const char* root_prefix)
55     : LinuxDumper(pid, root_prefix),
56       core_path_(core_path),
57       procfs_path_(procfs_path),
58       thread_infos_(&allocator_, 8) {
59   assert(core_path_);
60 }
61 
BuildProcPath(char * path,pid_t pid,const char * node) const62 bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
63                                     const char* node) const {
64   if (!path || !node)
65     return false;
66 
67   size_t node_len = my_strlen(node);
68   if (node_len == 0)
69     return false;
70 
71   size_t procfs_path_len = my_strlen(procfs_path_);
72   size_t total_length = procfs_path_len + 1 + node_len;
73   if (total_length >= NAME_MAX)
74     return false;
75 
76   memcpy(path, procfs_path_, procfs_path_len);
77   path[procfs_path_len] = '/';
78   memcpy(path + procfs_path_len + 1, node, node_len);
79   path[total_length] = '\0';
80   return true;
81 }
82 
CopyFromProcess(void * dest,pid_t child,const void * src,size_t length)83 bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
84                                       const void* src, size_t length) {
85   ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
86   // TODO(benchan): Investigate whether the data to be copied could span
87   // across multiple segments in the core dump file. ElfCoreDump::CopyData
88   // and this method do not handle that case yet.
89   if (!core_.CopyData(dest, virtual_address, length)) {
90     // If the data segment is not found in the core dump, fill the result
91     // with marker characters.
92     memset(dest, 0xab, length);
93     return false;
94   }
95   return true;
96 }
97 
GetThreadInfoByIndex(size_t index,ThreadInfo * info)98 bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
99   if (index >= thread_infos_.size())
100     return false;
101 
102   *info = thread_infos_[index];
103   const uint8_t* stack_pointer;
104 #if defined(__i386)
105   memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
106 #elif defined(__x86_64)
107   memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
108 #elif defined(__ARM_EABI__)
109   memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
110 #elif defined(__aarch64__)
111   memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
112 #elif defined(__mips__)
113   stack_pointer =
114       reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
115 #else
116 #error "This code hasn't been ported to your platform yet."
117 #endif
118   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
119   return true;
120 }
121 
IsPostMortem() const122 bool LinuxCoreDumper::IsPostMortem() const {
123   return true;
124 }
125 
ThreadsSuspend()126 bool LinuxCoreDumper::ThreadsSuspend() {
127   return true;
128 }
129 
ThreadsResume()130 bool LinuxCoreDumper::ThreadsResume() {
131   return true;
132 }
133 
EnumerateThreads()134 bool LinuxCoreDumper::EnumerateThreads() {
135   if (!mapped_core_file_.Map(core_path_, 0)) {
136     fprintf(stderr, "Could not map core dump file into memory\n");
137     return false;
138   }
139 
140   core_.SetContent(mapped_core_file_.content());
141   if (!core_.IsValid()) {
142     fprintf(stderr, "Invalid core dump file\n");
143     return false;
144   }
145 
146   ElfCoreDump::Note note = core_.GetFirstNote();
147   if (!note.IsValid()) {
148     fprintf(stderr, "PT_NOTE section not found\n");
149     return false;
150   }
151 
152   bool first_thread = true;
153   do {
154     ElfCoreDump::Word type = note.GetType();
155     MemoryRange name = note.GetName();
156     MemoryRange description = note.GetDescription();
157 
158     if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
159       fprintf(stderr, "Could not found a valid PT_NOTE.\n");
160       return false;
161     }
162 
163     // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
164     // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
165     //   Thread           Name          Type
166     //   -------------------------------------------------------------------
167     //   1st thread       CORE          NT_PRSTATUS
168     //   process-wide     CORE          NT_PRPSINFO
169     //   process-wide     CORE          NT_SIGINFO
170     //   process-wide     CORE          NT_AUXV
171     //   1st thread       CORE          NT_FPREGSET
172     //   1st thread       LINUX         NT_PRXFPREG
173     //   1st thread       LINUX         NT_386_TLS
174     //
175     //   2nd thread       CORE          NT_PRSTATUS
176     //   2nd thread       CORE          NT_FPREGSET
177     //   2nd thread       LINUX         NT_PRXFPREG
178     //   2nd thread       LINUX         NT_386_TLS
179     //
180     //   3rd thread       CORE          NT_PRSTATUS
181     //   3rd thread       CORE          NT_FPREGSET
182     //   3rd thread       LINUX         NT_PRXFPREG
183     //   3rd thread       LINUX         NT_386_TLS
184     //
185     // The following code only works if notes are ordered as expected.
186     switch (type) {
187       case NT_PRSTATUS: {
188         if (description.length() != sizeof(elf_prstatus)) {
189           fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
190           return false;
191         }
192 
193         const elf_prstatus* status =
194             reinterpret_cast<const elf_prstatus*>(description.data());
195         pid_t pid = status->pr_pid;
196         ThreadInfo info;
197         memset(&info, 0, sizeof(ThreadInfo));
198         info.tgid = status->pr_pgrp;
199         info.ppid = status->pr_ppid;
200 #if defined(__mips__)
201 #if defined(__ANDROID__)
202         for (int i = EF_R0; i <= EF_R31; i++)
203           info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
204 #else  // __ANDROID__
205         for (int i = EF_REG0; i <= EF_REG31; i++)
206           info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
207 #endif  // __ANDROID__
208         info.mcontext.mdlo = status->pr_reg[EF_LO];
209         info.mcontext.mdhi = status->pr_reg[EF_HI];
210         info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
211 #else  // __mips__
212         memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
213 #endif  // __mips__
214         if (first_thread) {
215           crash_thread_ = pid;
216           crash_signal_ = status->pr_info.si_signo;
217           crash_signal_code_ = status->pr_info.si_code;
218         }
219         first_thread = false;
220         threads_.push_back(pid);
221         thread_infos_.push_back(info);
222         break;
223       }
224       case NT_SIGINFO: {
225         if (description.length() != sizeof(siginfo_t)) {
226           fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n");
227           return false;
228         }
229 
230         const siginfo_t* info =
231             reinterpret_cast<const siginfo_t*>(description.data());
232 
233         // Set crash_address when si_addr is valid for the signal.
234         switch (info->si_signo) {
235           case MD_EXCEPTION_CODE_LIN_SIGBUS:
236           case MD_EXCEPTION_CODE_LIN_SIGFPE:
237           case MD_EXCEPTION_CODE_LIN_SIGILL:
238           case MD_EXCEPTION_CODE_LIN_SIGSEGV:
239           case MD_EXCEPTION_CODE_LIN_SIGSYS:
240           case MD_EXCEPTION_CODE_LIN_SIGTRAP:
241             crash_address_ = reinterpret_cast<uintptr_t>(info->si_addr);
242             break;
243         }
244 
245         // Set crash_exception_info for common signals.  Since exception info is
246         // unsigned, but some of these fields might be signed, we always cast.
247         switch (info->si_signo) {
248           case MD_EXCEPTION_CODE_LIN_SIGKILL:
249             set_crash_exception_info({
250               static_cast<uint64_t>(info->si_pid),
251               static_cast<uint64_t>(info->si_uid),
252             });
253             break;
254           case MD_EXCEPTION_CODE_LIN_SIGSYS:
255 #ifdef si_syscall
256             set_crash_exception_info({
257               static_cast<uint64_t>(info->si_syscall),
258               static_cast<uint64_t>(info->si_arch),
259             });
260 #endif
261             break;
262         }
263         break;
264       }
265 #if defined(__i386) || defined(__x86_64)
266       case NT_FPREGSET: {
267         if (thread_infos_.empty())
268           return false;
269 
270         ThreadInfo* info = &thread_infos_.back();
271         if (description.length() != sizeof(info->fpregs)) {
272           fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
273           return false;
274         }
275 
276         memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
277         break;
278       }
279 #endif
280 #if defined(__i386)
281       case NT_PRXFPREG: {
282         if (thread_infos_.empty())
283           return false;
284 
285         ThreadInfo* info = &thread_infos_.back();
286         if (description.length() != sizeof(info->fpxregs)) {
287           fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
288           return false;
289         }
290 
291         memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
292         break;
293       }
294 #endif
295     }
296     note = note.GetNextNote();
297   } while (note.IsValid());
298 
299   return true;
300 }
301 
302 }  // namespace google_breakpad
303