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