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 | |
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 | |
62 | 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 | |
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 | |
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 | |
122 | bool LinuxCoreDumper::IsPostMortem() const { |
123 | return true; |
124 | } |
125 | |
126 | bool LinuxCoreDumper::ThreadsSuspend() { |
127 | return true; |
128 | } |
129 | |
130 | bool LinuxCoreDumper::ThreadsResume() { |
131 | return true; |
132 | } |
133 | |
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 | char proc_mem_path[NAME_MAX]; |
141 | if (BuildProcPath(proc_mem_path, pid_, "mem" )) { |
142 | int fd = open(proc_mem_path, O_RDONLY | O_LARGEFILE | O_CLOEXEC); |
143 | if (fd != -1) { |
144 | core_.SetProcMem(fd); |
145 | } else { |
146 | fprintf(stderr, "Cannot open %s (%s)\n" , proc_mem_path, strerror(errno)); |
147 | } |
148 | } |
149 | |
150 | core_.SetContent(mapped_core_file_.content()); |
151 | if (!core_.IsValid()) { |
152 | fprintf(stderr, "Invalid core dump file\n" ); |
153 | return false; |
154 | } |
155 | |
156 | ElfCoreDump::Note note = core_.GetFirstNote(); |
157 | if (!note.IsValid()) { |
158 | fprintf(stderr, "PT_NOTE section not found\n" ); |
159 | return false; |
160 | } |
161 | |
162 | bool first_thread = true; |
163 | do { |
164 | ElfCoreDump::Word type = note.GetType(); |
165 | MemoryRange name = note.GetName(); |
166 | MemoryRange description = note.GetDescription(); |
167 | |
168 | if (type == 0 || name.IsEmpty() || description.IsEmpty()) { |
169 | fprintf(stderr, "Could not found a valid PT_NOTE.\n" ); |
170 | return false; |
171 | } |
172 | |
173 | // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are |
174 | // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): |
175 | // Thread Name Type |
176 | // ------------------------------------------------------------------- |
177 | // 1st thread CORE NT_PRSTATUS |
178 | // process-wide CORE NT_PRPSINFO |
179 | // process-wide CORE NT_SIGINFO |
180 | // process-wide CORE NT_AUXV |
181 | // 1st thread CORE NT_FPREGSET |
182 | // 1st thread LINUX NT_PRXFPREG |
183 | // 1st thread LINUX NT_386_TLS |
184 | // |
185 | // 2nd thread CORE NT_PRSTATUS |
186 | // 2nd thread CORE NT_FPREGSET |
187 | // 2nd thread LINUX NT_PRXFPREG |
188 | // 2nd thread LINUX NT_386_TLS |
189 | // |
190 | // 3rd thread CORE NT_PRSTATUS |
191 | // 3rd thread CORE NT_FPREGSET |
192 | // 3rd thread LINUX NT_PRXFPREG |
193 | // 3rd thread LINUX NT_386_TLS |
194 | // |
195 | // The following code only works if notes are ordered as expected. |
196 | switch (type) { |
197 | case NT_PRSTATUS: { |
198 | if (description.length() != sizeof(elf_prstatus)) { |
199 | fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n" ); |
200 | return false; |
201 | } |
202 | |
203 | const elf_prstatus* status = |
204 | reinterpret_cast<const elf_prstatus*>(description.data()); |
205 | pid_t pid = status->pr_pid; |
206 | ThreadInfo info; |
207 | memset(&info, 0, sizeof(ThreadInfo)); |
208 | info.tgid = status->pr_pgrp; |
209 | info.ppid = status->pr_ppid; |
210 | #if defined(__mips__) |
211 | #if defined(__ANDROID__) |
212 | for (int i = EF_R0; i <= EF_R31; i++) |
213 | info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; |
214 | #else // __ANDROID__ |
215 | for (int i = EF_REG0; i <= EF_REG31; i++) |
216 | info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i]; |
217 | #endif // __ANDROID__ |
218 | info.mcontext.mdlo = status->pr_reg[EF_LO]; |
219 | info.mcontext.mdhi = status->pr_reg[EF_HI]; |
220 | info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; |
221 | #else // __mips__ |
222 | memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); |
223 | #endif // __mips__ |
224 | if (first_thread) { |
225 | crash_thread_ = pid; |
226 | crash_signal_ = status->pr_info.si_signo; |
227 | crash_signal_code_ = status->pr_info.si_code; |
228 | } |
229 | first_thread = false; |
230 | threads_.push_back(pid); |
231 | thread_infos_.push_back(info); |
232 | break; |
233 | } |
234 | case NT_SIGINFO: { |
235 | if (description.length() != sizeof(siginfo_t)) { |
236 | fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n" ); |
237 | return false; |
238 | } |
239 | |
240 | const siginfo_t* info = |
241 | reinterpret_cast<const siginfo_t*>(description.data()); |
242 | |
243 | // Set crash_address when si_addr is valid for the signal. |
244 | switch (info->si_signo) { |
245 | case MD_EXCEPTION_CODE_LIN_SIGBUS: |
246 | case MD_EXCEPTION_CODE_LIN_SIGFPE: |
247 | case MD_EXCEPTION_CODE_LIN_SIGILL: |
248 | case MD_EXCEPTION_CODE_LIN_SIGSEGV: |
249 | case MD_EXCEPTION_CODE_LIN_SIGSYS: |
250 | case MD_EXCEPTION_CODE_LIN_SIGTRAP: |
251 | crash_address_ = reinterpret_cast<uintptr_t>(info->si_addr); |
252 | break; |
253 | } |
254 | |
255 | // Set crash_exception_info for common signals. Since exception info is |
256 | // unsigned, but some of these fields might be signed, we always cast. |
257 | switch (info->si_signo) { |
258 | case MD_EXCEPTION_CODE_LIN_SIGKILL: |
259 | set_crash_exception_info({ |
260 | static_cast<uint64_t>(info->si_pid), |
261 | static_cast<uint64_t>(info->si_uid), |
262 | }); |
263 | break; |
264 | case MD_EXCEPTION_CODE_LIN_SIGSYS: |
265 | #ifdef si_syscall |
266 | set_crash_exception_info({ |
267 | static_cast<uint64_t>(info->si_syscall), |
268 | static_cast<uint64_t>(info->si_arch), |
269 | }); |
270 | #endif |
271 | break; |
272 | } |
273 | break; |
274 | } |
275 | #if defined(__i386) || defined(__x86_64) |
276 | case NT_FPREGSET: { |
277 | if (thread_infos_.empty()) |
278 | return false; |
279 | |
280 | ThreadInfo* info = &thread_infos_.back(); |
281 | if (description.length() != sizeof(info->fpregs)) { |
282 | fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n" ); |
283 | return false; |
284 | } |
285 | |
286 | memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); |
287 | break; |
288 | } |
289 | #endif |
290 | #if defined(__i386) |
291 | case NT_PRXFPREG: { |
292 | if (thread_infos_.empty()) |
293 | return false; |
294 | |
295 | ThreadInfo* info = &thread_infos_.back(); |
296 | if (description.length() != sizeof(info->fpxregs)) { |
297 | fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n" ); |
298 | return false; |
299 | } |
300 | |
301 | memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); |
302 | break; |
303 | } |
304 | #endif |
305 | } |
306 | note = note.GetNextNote(); |
307 | } while (note.IsValid()); |
308 | |
309 | return true; |
310 | } |
311 | |
312 | } // namespace google_breakpad |
313 | |