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
49namespace google_breakpad {
50
51LinuxCoreDumper::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
62bool 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
83bool 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
98bool 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
122bool LinuxCoreDumper::IsPostMortem() const {
123 return true;
124}
125
126bool LinuxCoreDumper::ThreadsSuspend() {
127 return true;
128}
129
130bool LinuxCoreDumper::ThreadsResume() {
131 return true;
132}
133
134bool 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