/ src / client / linux / minidump_writer / linux_core_dumper.cc
linux_core_dumper.cc
  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  
 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  
 65  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  
 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  
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  
128  bool LinuxCoreDumper::IsPostMortem() const {
129    return true;
130  }
131  
132  bool LinuxCoreDumper::ThreadsSuspend() {
133    return true;
134  }
135  
136  bool LinuxCoreDumper::ThreadsResume() {
137    return true;
138  }
139  
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