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