xref: /aosp_15_r20/external/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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 
LinuxCoreDumper(pid_t pid,const char * core_path,const char * procfs_path,const char * root_prefix)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 
BuildProcPath(char * path,pid_t pid,const char * node) const65 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 
CopyFromProcess(void * dest,pid_t child,const void * src,size_t length)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 
GetThreadInfoByIndex(size_t index,ThreadInfo * info)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 
IsPostMortem() const128 bool LinuxCoreDumper::IsPostMortem() const {
129   return true;
130 }
131 
ThreadsSuspend()132 bool LinuxCoreDumper::ThreadsSuspend() {
133   return true;
134 }
135 
ThreadsResume()136 bool LinuxCoreDumper::ThreadsResume() {
137   return true;
138 }
139 
EnumerateThreads()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
322