1 // Copyright (c) 2013 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
16 namespace crazy {
17
18 namespace {
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.
ParseProcMapsLine(const char * line,const char * line_end,ProcMaps::Entry * entry)27 bool 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.
60 const char* tok_start = p;
61 const char* tok_end =
62 static_cast<const char*>(memchr(p, separator, line_end - p));
63 if (!tok_end) {
64 tok_end = line_end;
65 p = line_end;
66 } else {
67 p = tok_end + 1;
68 }
69
70 if (tok_end == tok_start) {
71 if (token == 6) {
72 // empty token can happen for index 6, when there is no path
73 // element on the line. This corresponds to anonymous memory
74 // mapped segments.
75 entry->path = NULL;
76 entry->path_len = 0;
77 break;
78 }
79 return false;
80 }
81
82 switch (token) {
83 case 0: // vma_start
84 entry->vma_start = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
85 break;
86
87 case 1: // vma_end
88 entry->vma_end = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
89 break;
90
91 case 2: // protection bits
92 {
93 int flags = 0;
94 for (const char* t = tok_start; t < tok_end; ++t) {
95 if (*t == 'r')
96 flags |= PROT_READ;
97 if (*t == 'w')
98 flags |= PROT_WRITE;
99 if (*t == 'x')
100 flags |= PROT_EXEC;
101 }
102 entry->prot_flags = flags;
103 } break;
104
105 case 3: // page offset
106 entry->load_offset =
107 static_cast<size_t>(strtoumax(tok_start, NULL, 16)) * PAGE_SIZE;
108 break;
109
110 case 6: // path
111 // Get rid of trailing newlines, if any.
112 while (tok_end > tok_start && tok_end[-1] == '\n')
113 tok_end--;
114 entry->path = tok_start;
115 entry->path_len = tok_end - tok_start;
116 break;
117
118 default: // ignore all other tokens.
119 ;
120 }
121 }
122 return true;
123 }
124
125 } // namespace
126
127 // Internal implementation of ProcMaps class.
128 class ProcMapsInternal {
129 public:
ProcMapsInternal()130 ProcMapsInternal() : index_(0), entries_() {}
131
~ProcMapsInternal()132 ~ProcMapsInternal() { Reset(); }
133
Open(const char * path)134 bool Open(const char* path) {
135 Reset();
136 LineReader reader(path);
137 index_ = 0;
138 while (reader.GetNextLine()) {
139 ProcMaps::Entry entry = {0, };
140 if (!ParseProcMapsLine(
141 reader.line(), reader.line() + reader.length(), &entry)) {
142 // Ignore broken lines.
143 continue;
144 }
145
146 // Reallocate path.
147 const char* old_path = entry.path;
148 if (old_path) {
149 char* new_path = static_cast<char*>(::malloc(entry.path_len + 1));
150 ::memcpy(new_path, old_path, entry.path_len);
151 new_path[entry.path_len] = '\0';
152 entry.path = const_cast<const char*>(new_path);
153 }
154
155 entries_.PushBack(entry);
156 }
157 return true;
158 }
159
Rewind()160 void Rewind() { index_ = 0; }
161
GetNextEntry(ProcMaps::Entry * entry)162 bool GetNextEntry(ProcMaps::Entry* entry) {
163 if (index_ >= entries_.GetCount())
164 return false;
165
166 *entry = entries_[index_++];
167 return true;
168 }
169
170 private:
Reset()171 void Reset() {
172 for (size_t n = 0; n < entries_.GetCount(); ++n) {
173 ProcMaps::Entry& entry = entries_[n];
174 ::free(const_cast<char*>(entry.path));
175 }
176 entries_.Resize(0);
177 }
178
179 size_t index_;
180 Vector<ProcMaps::Entry> entries_;
181 };
182
ProcMaps()183 ProcMaps::ProcMaps() {
184 internal_ = new ProcMapsInternal();
185 (void)internal_->Open("/proc/self/maps");
186 }
187
ProcMaps(pid_t pid)188 ProcMaps::ProcMaps(pid_t pid) {
189 internal_ = new ProcMapsInternal();
190 char maps_file[32];
191 snprintf(maps_file, sizeof maps_file, "/proc/%u/maps", pid);
192 (void)internal_->Open(maps_file);
193 }
194
~ProcMaps()195 ProcMaps::~ProcMaps() { delete internal_; }
196
Rewind()197 void ProcMaps::Rewind() { internal_->Rewind(); }
198
GetNextEntry(Entry * entry)199 bool ProcMaps::GetNextEntry(Entry* entry) {
200 return internal_->GetNextEntry(entry);
201 }
202
GetProtectionFlagsForAddress(void * address)203 int ProcMaps::GetProtectionFlagsForAddress(void* address) {
204 size_t vma_addr = reinterpret_cast<size_t>(address);
205 internal_->Rewind();
206 ProcMaps::Entry entry;
207 while (internal_->GetNextEntry(&entry)) {
208 if (entry.vma_start <= vma_addr && vma_addr < entry.vma_end)
209 return entry.prot_flags;
210 }
211 return 0;
212 }
213
FindElfBinaryForAddress(void * address,uintptr_t * load_address,char * path_buffer,size_t path_buffer_len)214 bool FindElfBinaryForAddress(void* address,
215 uintptr_t* load_address,
216 char* path_buffer,
217 size_t path_buffer_len) {
218 ProcMaps self_maps;
219 ProcMaps::Entry entry;
220
221 uintptr_t addr = reinterpret_cast<uintptr_t>(address);
222
223 while (self_maps.GetNextEntry(&entry)) {
224 if (entry.vma_start <= addr && addr < entry.vma_end) {
225 *load_address = entry.vma_start;
226 if (!entry.path) {
227 LOG("Could not find ELF binary path!?\n");
228 return false;
229 }
230 if (entry.path_len >= path_buffer_len) {
231 LOG("ELF binary path too long: '%s'\n", entry.path);
232 return false;
233 }
234 memcpy(path_buffer, entry.path, entry.path_len);
235 path_buffer[entry.path_len] = '\0';
236 return true;
237 }
238 }
239 return false;
240 }
241
242 // Returns the current protection bit flags for the page holding a given
243 // address. Returns true on success, or false if the address is not mapped.
FindProtectionFlagsForAddress(void * address,int * prot_flags)244 bool FindProtectionFlagsForAddress(void* address, int* prot_flags) {
245 ProcMaps self_maps;
246 ProcMaps::Entry entry;
247
248 uintptr_t addr = reinterpret_cast<uintptr_t>(address);
249
250 while (self_maps.GetNextEntry(&entry)) {
251 if (entry.vma_start <= addr && addr < entry.vma_end) {
252 *prot_flags = entry.prot_flags;
253 return true;
254 }
255 }
256 return false;
257 }
258
FindLoadAddressForFile(const char * file_name,uintptr_t * load_address,uintptr_t * load_offset)259 bool FindLoadAddressForFile(const char* file_name,
260 uintptr_t* load_address,
261 uintptr_t* load_offset) {
262 size_t file_name_len = strlen(file_name);
263 bool is_base_name = (strchr(file_name, '/') == NULL);
264 ProcMaps self_maps;
265 ProcMaps::Entry entry;
266
267 while (self_maps.GetNextEntry(&entry)) {
268 // Skip vDSO et al.
269 if (entry.path_len == 0 || entry.path[0] == '[')
270 continue;
271
272 const char* entry_name = entry.path;
273 size_t entry_len = entry.path_len;
274
275 if (is_base_name) {
276 const char* p = reinterpret_cast<const char*>(
277 ::memrchr(entry.path, '/', entry.path_len));
278 if (p) {
279 entry_name = p + 1;
280 entry_len = entry.path_len - (p - entry.path) - 1;
281 }
282 }
283
284 if (file_name_len == entry_len &&
285 !memcmp(file_name, entry_name, entry_len)) {
286 *load_address = entry.vma_start;
287 *load_offset = entry.load_offset;
288 return true;
289 }
290 }
291
292 return false;
293 }
294
295 } // namespace crazy
296