Home | History | Annotate | Download | only in src
      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