1// Copyright 2014 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 "crazy_linker_proc_maps.h"
6
7#include <inttypes.h>
8#include <limits.h>
9
10#include "elf_traits.h"
11#include "crazy_linker_debug.h"
12#include "crazy_linker_line_reader.h"
13#include "crazy_linker_util.h"
14#include "crazy_linker_system.h"
15
16namespace crazy {
17
18namespace {
19
20// Decompose the components of a /proc/$PID/maps file into multiple
21// components. |line| should be the address of a zero-terminated line
22// of input. On success, returns true and sets |*entry|, false otherwise.
23//
24// IMPORTANT: On success, |entry->path| will point into the input line,
25// the caller will have to copy the string into a different location if
26// it needs to persist it.
27bool ParseProcMapsLine(const char* line,
28                       const char* line_end,
29                       ProcMaps::Entry* entry) {
30  // Example input lines on a 64-bit system, one cannot assume that
31  // everything is properly sized.
32  //
33  // 00400000-0040b000 r-xp 00000000 08:01 6570708
34  // /bin/cat
35  // 0060a000-0060b000 r--p 0000a000 08:01 6570708
36  // /bin/cat
37  // 0060b000-0060c000 rw-p 0000b000 08:01 6570708
38  // /bin/cat
39  // 01dd0000-01df1000 rw-p 00000000 00:00 0
40  // [heap]
41  // 7f4b8d4d7000-7f4b8e22a000 r--p 00000000 08:01 38666648
42  // /usr/lib/locale/locale-archive
43  // 7f4b8e22a000-7f4b8e3df000 r-xp 00000000 08:01 28836281
44  // /lib/x86_64-linux-gnu/libc-2.15.so
45  // 7f4b8e3df000-7f4b8e5de000 ---p 001b5000 08:01 28836281
46  // /lib/x86_64-linux-gnu/libc-2.15.so
47  // 7f4b8e5de000-7f4b8e5e2000 r--p 001b4000 08:01 28836281
48  // /lib/x86_64-linux-gnu/libc-2.15.so
49  // 7f4b8e5e2000-7f4b8e5e4000 rw-p 001b8000 08:01 28836281
50  // /lib/x86_64-linux-gnu/libc-2.15.so
51  const char* p = line;
52  for (int token = 0; token < 7; ++token) {
53    char separator = (token == 0) ? '-' : ' ';
54    // skip leading token separators first.
55    while (p < line_end && *p == separator)
56      p++;
57
58    // find start and end of current token, and compute start of
59    // next search. The result of memchr(_,_,0) is undefined, treated as
60    // not-found.
61    const char* tok_start = p;
62    const size_t range = line_end - p;
63    const char* tok_end;
64    if (range > 0)
65      tok_end = static_cast<const char*>(memchr(p, separator, range));
66    else
67      tok_end = NULL;
68    if (!tok_end) {
69      tok_end = line_end;
70      p = line_end;
71    } else {
72      p = tok_end + 1;
73    }
74
75    if (tok_end == tok_start) {
76      if (token == 6) {
77        // empty token can happen for index 6, when there is no path
78        // element on the line. This corresponds to anonymous memory
79        // mapped segments.
80        entry->path = NULL;
81        entry->path_len = 0;
82        break;
83      }
84      return false;
85    }
86
87    switch (token) {
88      case 0:  // vma_start
89        entry->vma_start = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
90        break;
91
92      case 1:  // vma_end
93        entry->vma_end = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
94        break;
95
96      case 2:  // protection bits
97      {
98        int flags = 0;
99        for (const char* t = tok_start; t < tok_end; ++t) {
100          if (*t == 'r')
101            flags |= PROT_READ;
102          if (*t == 'w')
103            flags |= PROT_WRITE;
104          if (*t == 'x')
105            flags |= PROT_EXEC;
106        }
107        entry->prot_flags = flags;
108      } break;
109
110      case 3:  // page offset
111        entry->load_offset =
112            static_cast<size_t>(strtoumax(tok_start, NULL, 16)) * PAGE_SIZE;
113        break;
114
115      case 6:  // path
116        // Get rid of trailing newlines, if any.
117        while (tok_end > tok_start && tok_end[-1] == '\n')
118          tok_end--;
119        entry->path = tok_start;
120        entry->path_len = tok_end - tok_start;
121        break;
122
123      default:  // ignore all other tokens.
124        ;
125    }
126  }
127  return true;
128}
129
130}  // namespace
131
132// Internal implementation of ProcMaps class.
133class ProcMapsInternal {
134 public:
135  ProcMapsInternal() : index_(0), entries_() {}
136
137  ~ProcMapsInternal() { Reset(); }
138
139  bool Open(const char* path) {
140    Reset();
141    LineReader reader(path);
142    index_ = 0;
143    while (reader.GetNextLine()) {
144      ProcMaps::Entry entry = {0, };
145      if (!ParseProcMapsLine(
146               reader.line(), reader.line() + reader.length(), &entry)) {
147        // Ignore broken lines.
148        continue;
149      }
150
151      // Reallocate path.
152      const char* old_path = entry.path;
153      if (old_path) {
154        char* new_path = static_cast<char*>(::malloc(entry.path_len + 1));
155        ::memcpy(new_path, old_path, entry.path_len);
156        new_path[entry.path_len] = '\0';
157        entry.path = const_cast<const char*>(new_path);
158      }
159
160      entries_.PushBack(entry);
161    }
162    return true;
163  }
164
165  void Rewind() { index_ = 0; }
166
167  bool GetNextEntry(ProcMaps::Entry* entry) {
168    if (index_ >= entries_.GetCount())
169      return false;
170
171    *entry = entries_[index_++];
172    return true;
173  }
174
175 private:
176  void Reset() {
177    for (size_t n = 0; n < entries_.GetCount(); ++n) {
178      ProcMaps::Entry& entry = entries_[n];
179      ::free(const_cast<char*>(entry.path));
180    }
181    entries_.Resize(0);
182  }
183
184  size_t index_;
185  Vector<ProcMaps::Entry> entries_;
186};
187
188ProcMaps::ProcMaps() {
189  internal_ = new ProcMapsInternal();
190  (void)internal_->Open("/proc/self/maps");
191}
192
193ProcMaps::ProcMaps(pid_t pid) {
194  internal_ = new ProcMapsInternal();
195  char maps_file[32];
196  snprintf(maps_file, sizeof maps_file, "/proc/%u/maps", pid);
197  (void)internal_->Open(maps_file);
198}
199
200ProcMaps::~ProcMaps() { delete internal_; }
201
202void ProcMaps::Rewind() { internal_->Rewind(); }
203
204bool ProcMaps::GetNextEntry(Entry* entry) {
205  return internal_->GetNextEntry(entry);
206}
207
208int ProcMaps::GetProtectionFlagsForAddress(void* address) {
209  size_t vma_addr = reinterpret_cast<size_t>(address);
210  internal_->Rewind();
211  ProcMaps::Entry entry;
212  while (internal_->GetNextEntry(&entry)) {
213    if (entry.vma_start <= vma_addr && vma_addr < entry.vma_end)
214      return entry.prot_flags;
215  }
216  return 0;
217}
218
219bool FindElfBinaryForAddress(void* address,
220                             uintptr_t* load_address,
221                             char* path_buffer,
222                             size_t path_buffer_len) {
223  ProcMaps self_maps;
224  ProcMaps::Entry entry;
225
226  uintptr_t addr = reinterpret_cast<uintptr_t>(address);
227
228  while (self_maps.GetNextEntry(&entry)) {
229    if (entry.vma_start <= addr && addr < entry.vma_end) {
230      *load_address = entry.vma_start;
231      if (!entry.path) {
232        LOG("Could not find ELF binary path!?\n");
233        return false;
234      }
235      if (entry.path_len >= path_buffer_len) {
236        LOG("ELF binary path too long: '%s'\n", entry.path);
237        return false;
238      }
239      memcpy(path_buffer, entry.path, entry.path_len);
240      path_buffer[entry.path_len] = '\0';
241      return true;
242    }
243  }
244  return false;
245}
246
247// Returns the current protection bit flags for the page holding a given
248// address. Returns true on success, or false if the address is not mapped.
249bool FindProtectionFlagsForAddress(void* address, int* prot_flags) {
250  ProcMaps self_maps;
251  ProcMaps::Entry entry;
252
253  uintptr_t addr = reinterpret_cast<uintptr_t>(address);
254
255  while (self_maps.GetNextEntry(&entry)) {
256    if (entry.vma_start <= addr && addr < entry.vma_end) {
257      *prot_flags = entry.prot_flags;
258      return true;
259    }
260  }
261  return false;
262}
263
264bool FindLoadAddressForFile(const char* file_name,
265                            uintptr_t* load_address,
266                            uintptr_t* load_offset) {
267  size_t file_name_len = strlen(file_name);
268  bool is_base_name = (strchr(file_name, '/') == NULL);
269  ProcMaps self_maps;
270  ProcMaps::Entry entry;
271
272  while (self_maps.GetNextEntry(&entry)) {
273    // Skip vDSO et al.
274    if (entry.path_len == 0 || entry.path[0] == '[')
275      continue;
276
277    const char* entry_name = entry.path;
278    size_t entry_len = entry.path_len;
279
280    if (is_base_name) {
281      const char* p = reinterpret_cast<const char*>(
282          ::memrchr(entry.path, '/', entry.path_len));
283      if (p) {
284        entry_name = p + 1;
285        entry_len = entry.path_len - (p - entry.path) - 1;
286      }
287    }
288
289    if (file_name_len == entry_len &&
290        !memcmp(file_name, entry_name, entry_len)) {
291      *load_address = entry.vma_start;
292      *load_offset = entry.load_offset;
293      return true;
294    }
295  }
296
297  return false;
298}
299
300}  // namespace crazy
301