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