aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/minidump_writer/linux_dumper.h
blob: 7bee160f1aba9d34adf3c328fa92497505ff7421 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
// is a base class for extracting information of a crashed process. It
// was originally a complete implementation using the ptrace API, but
// has been refactored to allow derived implementations supporting both
// ptrace and core dump. A portion of the original implementation is now
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
// details).

#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_

#include <assert.h>
#include <elf.h>
#if defined(__ANDROID__)
#include <link.h>
#endif
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>

#include <vector>

#include "client/linux/dump_writer_common/mapping_info.h"
#include "client/linux/dump_writer_common/thread_info.h"
#include "common/linux/file_id.h"
#include "common/memory_allocator.h"
#include "google_breakpad/common/minidump_format.h"

namespace google_breakpad {

// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386) || defined(__ARM_EABI__) || \
 (defined(__mips__) && _MIPS_SIM == _ABIO32)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64) || defined(__aarch64__) || \
     (defined(__mips__) && _MIPS_SIM != _ABIO32)
typedef Elf64_auxv_t elf_aux_entry;
#endif

typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;

// When we find the VDSO mapping in the process's address space, this
// is the name we use for it when writing it to the minidump.
// This should always be less than NAME_MAX!
const char kLinuxGateLibraryName[] = "linux-gate.so";

class LinuxDumper {
 public:
  // The |root_prefix| is prepended to mapping paths before opening them, which
  // is useful if the crash originates from a chroot.
  explicit LinuxDumper(pid_t pid, const char* root_prefix = "");

  virtual ~LinuxDumper();

  // Parse the data for |threads| and |mappings|.
  virtual bool Init();

  // Take any actions that could not be taken in Init(). LateInit() is
  // called after all other caller's initialization is complete, and in
  // particular after it has called ThreadsSuspend(), so that ptrace is
  // available.
  virtual bool LateInit();

  // Return true if the dumper performs a post-mortem dump.
  virtual bool IsPostMortem() const = 0;

  // Suspend/resume all threads in the given process.
  virtual bool ThreadsSuspend() = 0;
  virtual bool ThreadsResume() = 0;

  // Read information about the |index|-th thread of |threads_|.
  // Returns true on success. One must have called |ThreadsSuspend| first.
  virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;

  size_t GetMainThreadIndex() const {
    for (size_t i = 0; i < threads_.size(); ++i) {
      if (threads_[i] == pid_) return i;
    }
    return -1u;
  }

  // These are only valid after a call to |Init|.
  const wasteful_vector<pid_t>& threads() { return threads_; }
  const wasteful_vector<MappingInfo*>& mappings() { return mappings_; }
  const MappingInfo* FindMapping(const void* address) const;
  // Find the mapping which the given memory address falls in. Unlike
  // FindMapping, this method uses the unadjusted mapping address
  // ranges from the kernel, rather than the ranges that have had the
  // load bias applied.
  const MappingInfo* FindMappingNoBias(uintptr_t address) const;
  const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }

  // Find a block of memory to take as the stack given the top of stack pointer.
  //   stack: (output) the lowest address in the memory area
  //   stack_len: (output) the length of the memory area
  //   stack_top: the current top of the stack
  bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);

  // Sanitize a copy of the stack by overwriting words that are not
  // pointers with a sentinel (0x0defaced).
  //   stack_copy: a copy of the stack to sanitize. |stack_copy| might
  //               not be word aligned, but it represents word aligned
  //               data copied from another location.
  //   stack_len: the length of the allocation pointed to by |stack_copy|.
  //   stack_pointer: the address of the stack pointer (used to locate
  //                  the stack mapping, as an optimization).
  //   sp_offset: the offset relative to stack_copy that reflects the
  //              current value of the stack pointer.
  void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
                         uintptr_t stack_pointer, uintptr_t sp_offset);

  // Test whether |stack_copy| contains a pointer-aligned word that
  // could be an address within a given mapping.
  //   stack_copy: a copy of the stack to check. |stack_copy| might
  //               not be word aligned, but it represents word aligned
  //               data copied from another location.
  //   stack_len: the length of the allocation pointed to by |stack_copy|.
  //   sp_offset: the offset relative to stack_copy that reflects the
  //              current value of the stack pointer.
  //   mapping: the mapping against which to test stack words.
  bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len,
                                uintptr_t sp_offset,
                                const MappingInfo& mapping);

  PageAllocator* allocator() { return &allocator_; }

  // Copy content of |length| bytes from a given process |child|,
  // starting from |src|, into |dest|. Returns true on success.
  virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
                               size_t length) = 0;

  // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
  // |path| is a character array of at least NAME_MAX bytes to return the
  // result.|node| is the final node without any slashes. Returns true on
  // success.
  virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;

  // Generate a File ID from the .text section of a mapped entry.
  // If not a member, mapping_id is ignored. This method can also manipulate the
  // |mapping|.name to truncate "(deleted)" from the file name if necessary.
  bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
                                   bool member,
                                   unsigned int mapping_id,
                                   wasteful_vector<uint8_t>& identifier);

  void SetCrashInfoFromSigInfo(const siginfo_t& siginfo);

  uintptr_t crash_address() const { return crash_address_; }
  void set_crash_address(uintptr_t crash_address) {
    crash_address_ = crash_address;
  }

  int crash_signal() const { return crash_signal_; }
  void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
  const char* GetCrashSignalString() const;

  void set_crash_signal_code(int code) { crash_signal_code_ = code; }
  int crash_signal_code() const { return crash_signal_code_; }

  void set_crash_exception_info(const std::vector<uint64_t>& exception_info) {
    assert(exception_info.size() <= MD_EXCEPTION_MAXIMUM_PARAMETERS);
    crash_exception_info_ = exception_info;
  }
  const std::vector<uint64_t>& crash_exception_info() const {
    return crash_exception_info_;
  }

  pid_t crash_thread() const { return crash_thread_; }
  void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }

  // Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and
  // returns true unless the string is too long.
  bool GetMappingAbsolutePath(const MappingInfo& mapping,
                              char path[PATH_MAX]) const;

  // Extracts the effective path and file name of from |mapping|. In most cases
  // the effective name/path are just the mapping's path and basename. In some
  // other cases, however, a library can be mapped from an archive (e.g., when
  // loading .so libs from an apk on Android) and this method is able to
  // reconstruct the original file name.
  void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
                                      char* file_path,
                                      size_t file_path_size,
                                      char* file_name,
                                      size_t file_name_size);

 protected:
  bool ReadAuxv();

  virtual bool EnumerateMappings();

  virtual bool EnumerateThreads() = 0;

  // For the case where a running program has been deleted, it'll show up in
  // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
  // see if '/path/to/program (deleted)' matches /proc/pid/exe and return
  // /proc/pid/exe in |path| so ELF identifier generation works correctly. This
  // also checks to see if '/path/to/program (deleted)' exists, so it does not
  // get fooled by a poorly named binary.
  // For programs that don't end with ' (deleted)', this is a no-op.
  // This assumes |path| is a buffer with length NAME_MAX.
  // Returns true if |path| is modified.
  bool HandleDeletedFileInMapping(char* path) const;

   // ID of the crashed process.
  const pid_t pid_;

  // Path of the root directory to which mapping paths are relative.
  const char* const root_prefix_;

  // Virtual address at which the process crashed.
  uintptr_t crash_address_;

  // Signal that terminated the crashed process.
  int crash_signal_;

  // The code associated with |crash_signal_|.
  int crash_signal_code_;

  // The additional fields associated with |crash_signal_|.
  std::vector<uint64_t> crash_exception_info_;

  // ID of the crashed thread.
  pid_t crash_thread_;

  mutable PageAllocator allocator_;

  // IDs of all the threads.
  wasteful_vector<pid_t> threads_;

  // Info from /proc/<pid>/maps.
  wasteful_vector<MappingInfo*> mappings_;

  // Info from /proc/<pid>/auxv
  wasteful_vector<elf_aux_val_t> auxv_;

#if defined(__ANDROID__)
 private:
  // Android M and later support packed ELF relocations in shared libraries.
  // Packing relocations changes the vaddr of the LOAD segments, such that
  // the effective load bias is no longer the same as the start address of
  // the memory mapping containing the executable parts of the library. The
  // packing is applied to the stripped library run on the target, but not to
  // any other library, and in particular not to the library used to generate
  // breakpad symbols. As a result, we need to adjust the |start_addr| for
  // any mapping that results from a shared library that contains Android
  // packed relocations, so that it properly represents the effective library
  // load bias. The following functions support this adjustment.

  // Check that a given mapping at |start_addr| is for an ELF shared library.
  // If it is, place the ELF header in |ehdr| and return true.
  // The first LOAD segment in an ELF shared library has offset zero, so the
  // ELF file header is at the start of this map entry, and in already mapped
  // memory.
  bool GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr);

  // For the ELF file mapped at |start_addr|, iterate ELF program headers to
  // find the min vaddr of all program header LOAD segments, the vaddr for
  // the DYNAMIC segment, and a count of DYNAMIC entries. Return values in
  // |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|.
  // The program header table is also in already mapped memory.
  void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
                                    uintptr_t start_addr,
                                    uintptr_t* min_vaddr_ptr,
                                    uintptr_t* dyn_vaddr_ptr,
                                    size_t* dyn_count_ptr);

  // Search the DYNAMIC tags for the ELF file with the given |load_bias|, and
  // return true if the tags indicate that the file contains Android packed
  // relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|.
  bool HasAndroidPackedRelocations(uintptr_t load_bias,
                                   uintptr_t dyn_vaddr,
                                   size_t dyn_count);

  // If the ELF file mapped at |start_addr| contained Android packed
  // relocations, return the load bias that the system linker (or Chromium
  // crazy linker) will have used. If the file did not contain Android
  // packed relocations, returns |start_addr|, indicating that no adjustment
  // is necessary.
  // The effective load bias is |start_addr| adjusted downwards by the
  // min vaddr in the library LOAD segments.
  uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr);

  // Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr|
  // field of any that represent ELF shared libraries with Android packed
  // relocations, so that |start_addr| is the load bias that the system linker
  // (or Chromium crazy linker) used. This value matches the addresses produced
  // when the non-relocation-packed library is used for breakpad symbol
  // generation.
  void LatePostprocessMappings();
#endif  // __ANDROID__
};

}  // namespace google_breakpad

#endif  // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_