1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/debug/stack_trace.h"
6 
7 #include <elf.h>
8 #include <link.h>
9 #include <stddef.h>
10 #include <threads.h>
11 #include <unwind.h>
12 #include <zircon/process.h>
13 #include <zircon/syscalls.h>
14 #include <zircon/syscalls/port.h>
15 #include <zircon/types.h>
16 
17 #include <algorithm>
18 #include <array>
19 #include <iomanip>
20 #include <iostream>
21 #include <type_traits>
22 
23 #include "base/atomic_sequence_num.h"
24 #include "base/debug/elf_reader.h"
25 #include "base/logging.h"
26 #include "base/no_destructor.h"
27 #include "base/stl_util.h"
28 
29 namespace base {
30 namespace debug {
31 namespace {
32 
33 struct BacktraceData {
34   void** trace_array;
35   size_t* count;
36   size_t max;
37 };
38 
UnwindStore(struct _Unwind_Context * context,void * user_data)39 _Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context,
40                                 void* user_data) {
41   BacktraceData* data = reinterpret_cast<BacktraceData*>(user_data);
42   uintptr_t pc = _Unwind_GetIP(context);
43   data->trace_array[*data->count] = reinterpret_cast<void*>(pc);
44   *data->count += 1;
45   if (*data->count == data->max)
46     return _URC_END_OF_STACK;
47   return _URC_NO_REASON;
48 }
49 
50 // Build a "rwx" C string-based representation of the permission bits.
51 // The output buffer is reused across calls, and should not be retained across
52 // consecutive invocations of this function.
PermissionFlagsToString(int flags,char permission_buf[4])53 const char* PermissionFlagsToString(int flags, char permission_buf[4]) {
54   char* permission = permission_buf;
55 
56   if (flags & PF_R)
57     (*permission++) = 'r';
58 
59   if (flags & PF_W)
60     (*permission++) = 'w';
61 
62   if (flags & PF_X)
63     (*permission++) = 'x';
64 
65   *permission = '\0';
66 
67   return permission_buf;
68 }
69 
70 // Stores and queries debugging symbol map info for the current process.
71 class SymbolMap {
72  public:
73   struct Segment {
74     const void* addr = nullptr;
75     size_t relative_addr = 0;
76     int permission_flags = 0;
77     size_t size = 0;
78   };
79 
80   struct Module {
81     // Maximum number of PT_LOAD segments to process per ELF binary. Most
82     // binaries have only 2-3 such segments.
83     static constexpr size_t kMaxSegmentCount = 8;
84 
85     const void* addr = nullptr;
86     std::array<Segment, kMaxSegmentCount> segments;
87     size_t segment_count = 0;
88     char name[ZX_MAX_NAME_LEN + 1] = {0};
89     char build_id[kMaxBuildIdStringLength + 1] = {0};
90   };
91 
92   SymbolMap();
93   ~SymbolMap() = default;
94 
95   // Gets all entries for the symbol map.
GetModules()96   span<Module> GetModules() { return {modules_.data(), count_}; }
97 
98  private:
99   // Component builds of Chrome pull about 250 shared libraries (on Linux), so
100   // 512 entries should be enough in most cases.
101   static const size_t kMaxMapEntries = 512;
102 
103   void Populate();
104 
105   // Sorted in descending order by address, for lookup purposes.
106   std::array<Module, kMaxMapEntries> modules_;
107 
108   size_t count_ = 0;
109   bool valid_ = false;
110 
111   DISALLOW_COPY_AND_ASSIGN(SymbolMap);
112 };
113 
SymbolMap()114 SymbolMap::SymbolMap() {
115   Populate();
116 }
117 
Populate()118 void SymbolMap::Populate() {
119   zx_handle_t process = zx_process_self();
120 
121   // Try to fetch the name of the process' main executable, which was set as the
122   // name of the |process| kernel object.
123   // TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so
124   // if we keep hitting problems with truncation, find a way to plumb argv[0]
125   // through to here instead, e.g. using CommandLine::GetProgramName().
126   char app_name[std::extent<decltype(SymbolMap::Module::name)>()];
127   zx_status_t status =
128       zx_object_get_property(process, ZX_PROP_NAME, app_name, sizeof(app_name));
129   if (status == ZX_OK) {
130     // The process name may have a process type suffix at the end (e.g.
131     // "context", "renderer", gpu"), which doesn't belong in the module list.
132     // Trim the suffix from the name.
133     for (size_t i = 0; i < base::size(app_name) && app_name[i] != '\0'; ++i) {
134       if (app_name[i] == ':') {
135         app_name[i] = 0;
136         break;
137       }
138     }
139   } else {
140     DPLOG(WARNING)
141         << "Couldn't get name, falling back to 'app' for program name: "
142         << status;
143     strlcat(app_name, "app", sizeof(app_name));
144   }
145 
146   // Retrieve the debug info struct.
147   uintptr_t debug_addr;
148   status = zx_object_get_property(process, ZX_PROP_PROCESS_DEBUG_ADDR,
149                                   &debug_addr, sizeof(debug_addr));
150   if (status != ZX_OK) {
151     DPLOG(ERROR) << "Couldn't get symbol map for process: " << status;
152     return;
153   }
154   r_debug* debug_info = reinterpret_cast<r_debug*>(debug_addr);
155 
156   // Get the link map from the debug info struct.
157   link_map* lmap = reinterpret_cast<link_map*>(debug_info->r_map);
158   if (!lmap) {
159     DPLOG(ERROR) << "Null link_map for process.";
160     return;
161   }
162 
163   // Populate ELF binary metadata into |modules_|.
164   while (lmap != nullptr) {
165     if (count_ >= kMaxMapEntries)
166       break;
167 
168     SymbolMap::Module& next_entry = modules_[count_];
169     ++count_;
170 
171     next_entry.addr = reinterpret_cast<void*>(lmap->l_addr);
172 
173     // Create Segment sub-entries for all PT_LOAD headers.
174     // Each Segment corresponds to a "mmap" line in the output.
175     next_entry.segment_count = 0;
176     for (const Elf64_Phdr& phdr : GetElfProgramHeaders(next_entry.addr)) {
177       if (phdr.p_type != PT_LOAD)
178         continue;
179 
180       if (next_entry.segment_count > Module::kMaxSegmentCount) {
181         LOG(WARNING) << "Exceeded the maximum number of segments.";
182         break;
183       }
184 
185       Segment segment;
186       segment.addr =
187           reinterpret_cast<const char*>(next_entry.addr) + phdr.p_vaddr;
188       segment.relative_addr = phdr.p_vaddr;
189       segment.size = phdr.p_memsz;
190       segment.permission_flags = phdr.p_flags;
191 
192       next_entry.segments[next_entry.segment_count] = std::move(segment);
193       ++next_entry.segment_count;
194     }
195 
196     // Get the human-readable library name from the ELF header, falling back on
197     // using names from the link map for binaries that aren't shared libraries.
198     Optional<StringPiece> elf_library_name =
199         ReadElfLibraryName(next_entry.addr);
200     if (elf_library_name) {
201       strlcpy(next_entry.name, elf_library_name->data(),
202               elf_library_name->size() + 1);
203     } else {
204       StringPiece link_map_name(lmap->l_name[0] ? lmap->l_name : app_name);
205 
206       // The "module" stack trace annotation doesn't allow for strings which
207       // resemble paths, so extract the filename portion from |link_map_name|.
208       size_t directory_prefix_idx = link_map_name.find_last_of("/");
209       if (directory_prefix_idx != StringPiece::npos) {
210         link_map_name = link_map_name.substr(
211             directory_prefix_idx + 1,
212             link_map_name.size() - directory_prefix_idx - 1);
213       }
214       strlcpy(next_entry.name, link_map_name.data(), link_map_name.size() + 1);
215     }
216 
217     if (!ReadElfBuildId(next_entry.addr, false, next_entry.build_id)) {
218       LOG(WARNING) << "Couldn't read build ID.";
219       continue;
220     }
221 
222     lmap = lmap->l_next;
223   }
224 
225   valid_ = true;
226 }
227 
228 }  // namespace
229 
230 // static
EnableInProcessStackDumping()231 bool EnableInProcessStackDumping() {
232   // StackTrace works to capture the current stack (e.g. for diagnostics added
233   // to code), but for local capture and print of backtraces, we just let the
234   // system crashlogger take over. It handles printing out a nicely formatted
235   // backtrace with dso information, relative offsets, etc. that we can then
236   // filter with addr2line in the run script to get file/line info.
237   return true;
238 }
239 
CollectStackTrace(void ** trace,size_t count)240 size_t CollectStackTrace(void** trace, size_t count) {
241   size_t frame_count = 0;
242   BacktraceData data = {trace, &frame_count, count};
243   _Unwind_Backtrace(&UnwindStore, &data);
244   return frame_count;
245 }
246 
PrintWithPrefix(const char * prefix_string) const247 void StackTrace::PrintWithPrefix(const char* prefix_string) const {
248   OutputToStreamWithPrefix(&std::cerr, prefix_string);
249 }
250 
251 // Emits stack trace data using the symbolizer markup format specified at:
252 // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
OutputToStreamWithPrefix(std::ostream * os,const char * prefix_string) const253 void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
254                                           const char* prefix_string) const {
255   SymbolMap map;
256 
257   int module_id = 0;
258   *os << "{{{reset}}}\n";
259   for (const SymbolMap::Module& entry : map.GetModules()) {
260     *os << "{{{module:" << module_id << ":" << entry.name
261         << ":elf:" << entry.build_id << "}}}\n";
262 
263     for (size_t i = 0; i < entry.segment_count; ++i) {
264       const SymbolMap::Segment& segment = entry.segments[i];
265 
266       char permission_string[4] = {};
267       *os << "{{{mmap:" << segment.addr << ":0x" << std::hex << segment.size
268           << std::dec << ":load:" << module_id << ":"
269           << PermissionFlagsToString(segment.permission_flags,
270                                      permission_string)
271           << ":"
272           << "0x" << std::hex << segment.relative_addr << std::dec << "}}}\n";
273     }
274 
275     ++module_id;
276   }
277 
278   for (size_t i = 0; i < count_; ++i)
279     *os << "{{{bt:" << i << ":" << trace_[i] << "}}}\n";
280 }
281 
282 }  // namespace debug
283 }  // namespace base
284