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