• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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