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 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: 130 ProcMapsInternal() : index_(0), entries_() {} 131 132 ~ProcMapsInternal() { Reset(); } 133 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 160 void Rewind() { index_ = 0; } 161 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: 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 183 ProcMaps::ProcMaps() { 184 internal_ = new ProcMapsInternal(); 185 (void)internal_->Open("/proc/self/maps"); 186 } 187 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 195 ProcMaps::~ProcMaps() { delete internal_; } 196 197 void ProcMaps::Rewind() { internal_->Rewind(); } 198 199 bool ProcMaps::GetNextEntry(Entry* entry) { 200 return internal_->GetNextEntry(entry); 201 } 202 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 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. 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 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