Home | History | Annotate | Download | only in sanitizer_common
      1 //===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // Information about the process mappings (Linux-specific parts).
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "sanitizer_platform.h"
     14 #if SANITIZER_FREEBSD || SANITIZER_LINUX
     15 #include "sanitizer_common.h"
     16 #include "sanitizer_placement_new.h"
     17 #include "sanitizer_procmaps.h"
     18 
     19 #if SANITIZER_FREEBSD
     20 #include <unistd.h>
     21 #include <sys/sysctl.h>
     22 #include <sys/user.h>
     23 #endif
     24 
     25 namespace __sanitizer {
     26 
     27 // Linker initialized.
     28 ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
     29 StaticSpinMutex MemoryMappingLayout::cache_lock_;  // Linker initialized.
     30 
     31 static void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
     32 #if SANITIZER_FREEBSD
     33   const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
     34   size_t Size = 0;
     35   int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
     36   CHECK_EQ(Err, 0);
     37   CHECK_GT(Size, 0);
     38 
     39   size_t MmapedSize = Size * 4 / 3;
     40   void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
     41   Size = MmapedSize;
     42   Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
     43   CHECK_EQ(Err, 0);
     44 
     45   proc_maps->data = (char*)VmMap;
     46   proc_maps->mmaped_size = MmapedSize;
     47   proc_maps->len = Size;
     48 #else
     49   proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
     50                                     &proc_maps->mmaped_size, 1 << 26);
     51 #endif
     52 }
     53 
     54 MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
     55   ReadProcMaps(&proc_self_maps_);
     56   if (cache_enabled) {
     57     if (proc_self_maps_.mmaped_size == 0) {
     58       LoadFromCache();
     59       CHECK_GT(proc_self_maps_.len, 0);
     60     }
     61   } else {
     62     CHECK_GT(proc_self_maps_.mmaped_size, 0);
     63   }
     64   Reset();
     65   // FIXME: in the future we may want to cache the mappings on demand only.
     66   if (cache_enabled)
     67     CacheMemoryMappings();
     68 }
     69 
     70 MemoryMappingLayout::~MemoryMappingLayout() {
     71   // Only unmap the buffer if it is different from the cached one. Otherwise
     72   // it will be unmapped when the cache is refreshed.
     73   if (proc_self_maps_.data != cached_proc_self_maps_.data) {
     74     UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
     75   }
     76 }
     77 
     78 void MemoryMappingLayout::Reset() {
     79   current_ = proc_self_maps_.data;
     80 }
     81 
     82 // static
     83 void MemoryMappingLayout::CacheMemoryMappings() {
     84   SpinMutexLock l(&cache_lock_);
     85   // Don't invalidate the cache if the mappings are unavailable.
     86   ProcSelfMapsBuff old_proc_self_maps;
     87   old_proc_self_maps = cached_proc_self_maps_;
     88   ReadProcMaps(&cached_proc_self_maps_);
     89   if (cached_proc_self_maps_.mmaped_size == 0) {
     90     cached_proc_self_maps_ = old_proc_self_maps;
     91   } else {
     92     if (old_proc_self_maps.mmaped_size) {
     93       UnmapOrDie(old_proc_self_maps.data,
     94                  old_proc_self_maps.mmaped_size);
     95     }
     96   }
     97 }
     98 
     99 void MemoryMappingLayout::LoadFromCache() {
    100   SpinMutexLock l(&cache_lock_);
    101   if (cached_proc_self_maps_.data) {
    102     proc_self_maps_ = cached_proc_self_maps_;
    103   }
    104 }
    105 
    106 #if !SANITIZER_FREEBSD
    107 // Parse a hex value in str and update str.
    108 static uptr ParseHex(char **str) {
    109   uptr x = 0;
    110   char *s;
    111   for (s = *str; ; s++) {
    112     char c = *s;
    113     uptr v = 0;
    114     if (c >= '0' && c <= '9')
    115       v = c - '0';
    116     else if (c >= 'a' && c <= 'f')
    117       v = c - 'a' + 10;
    118     else if (c >= 'A' && c <= 'F')
    119       v = c - 'A' + 10;
    120     else
    121       break;
    122     x = x * 16 + v;
    123   }
    124   *str = s;
    125   return x;
    126 }
    127 
    128 static bool IsOneOf(char c, char c1, char c2) {
    129   return c == c1 || c == c2;
    130 }
    131 #endif
    132 
    133 static bool IsDecimal(char c) {
    134   return c >= '0' && c <= '9';
    135 }
    136 
    137 static bool IsHex(char c) {
    138   return (c >= '0' && c <= '9')
    139       || (c >= 'a' && c <= 'f');
    140 }
    141 
    142 static uptr ReadHex(const char *p) {
    143   uptr v = 0;
    144   for (; IsHex(p[0]); p++) {
    145     if (p[0] >= '0' && p[0] <= '9')
    146       v = v * 16 + p[0] - '0';
    147     else
    148       v = v * 16 + p[0] - 'a' + 10;
    149   }
    150   return v;
    151 }
    152 
    153 static uptr ReadDecimal(const char *p) {
    154   uptr v = 0;
    155   for (; IsDecimal(p[0]); p++)
    156     v = v * 10 + p[0] - '0';
    157   return v;
    158 }
    159 
    160 bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
    161                                char filename[], uptr filename_size,
    162                                uptr *protection) {
    163   char *last = proc_self_maps_.data + proc_self_maps_.len;
    164   if (current_ >= last) return false;
    165   uptr dummy;
    166   if (!start) start = &dummy;
    167   if (!end) end = &dummy;
    168   if (!offset) offset = &dummy;
    169   if (!protection) protection = &dummy;
    170 #if SANITIZER_FREEBSD
    171   struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
    172 
    173   *start = (uptr)VmEntry->kve_start;
    174   *end = (uptr)VmEntry->kve_end;
    175   *offset = (uptr)VmEntry->kve_offset;
    176 
    177   *protection = 0;
    178   if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
    179     *protection |= kProtectionRead;
    180   if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
    181     *protection |= kProtectionWrite;
    182   if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
    183     *protection |= kProtectionExecute;
    184 
    185   if (filename != NULL && filename_size > 0) {
    186     internal_snprintf(filename,
    187                       Min(filename_size, (uptr)PATH_MAX),
    188                       "%s", VmEntry->kve_path);
    189   }
    190 
    191   current_ += VmEntry->kve_structsize;
    192 #else  // !SANITIZER_FREEBSD
    193   char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
    194   if (next_line == 0)
    195     next_line = last;
    196   // Example: 08048000-08056000 r-xp 00000000 03:0c 64593   /foo/bar
    197   *start = ParseHex(&current_);
    198   CHECK_EQ(*current_++, '-');
    199   *end = ParseHex(&current_);
    200   CHECK_EQ(*current_++, ' ');
    201   CHECK(IsOneOf(*current_, '-', 'r'));
    202   *protection = 0;
    203   if (*current_++ == 'r')
    204     *protection |= kProtectionRead;
    205   CHECK(IsOneOf(*current_, '-', 'w'));
    206   if (*current_++ == 'w')
    207     *protection |= kProtectionWrite;
    208   CHECK(IsOneOf(*current_, '-', 'x'));
    209   if (*current_++ == 'x')
    210     *protection |= kProtectionExecute;
    211   CHECK(IsOneOf(*current_, 's', 'p'));
    212   if (*current_++ == 's')
    213     *protection |= kProtectionShared;
    214   CHECK_EQ(*current_++, ' ');
    215   *offset = ParseHex(&current_);
    216   CHECK_EQ(*current_++, ' ');
    217   ParseHex(&current_);
    218   CHECK_EQ(*current_++, ':');
    219   ParseHex(&current_);
    220   CHECK_EQ(*current_++, ' ');
    221   while (IsDecimal(*current_))
    222     current_++;
    223   // Qemu may lack the trailing space.
    224   // http://code.google.com/p/address-sanitizer/issues/detail?id=160
    225   // CHECK_EQ(*current_++, ' ');
    226   // Skip spaces.
    227   while (current_ < next_line && *current_ == ' ')
    228     current_++;
    229   // Fill in the filename.
    230   uptr i = 0;
    231   while (current_ < next_line) {
    232     if (filename && i < filename_size - 1)
    233       filename[i++] = *current_;
    234     current_++;
    235   }
    236   if (filename && i < filename_size)
    237     filename[i] = 0;
    238   current_ = next_line + 1;
    239 #endif  // !SANITIZER_FREEBSD
    240   return true;
    241 }
    242 
    243 uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
    244                                             uptr max_modules,
    245                                             string_predicate_t filter) {
    246   Reset();
    247   uptr cur_beg, cur_end, cur_offset, prot;
    248   InternalScopedBuffer<char> module_name(kMaxPathLength);
    249   uptr n_modules = 0;
    250   for (uptr i = 0; n_modules < max_modules &&
    251                        Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
    252                             module_name.size(), &prot);
    253        i++) {
    254     const char *cur_name = module_name.data();
    255     if (cur_name[0] == '\0')
    256       continue;
    257     if (filter && !filter(cur_name))
    258       continue;
    259     void *mem = &modules[n_modules];
    260     // Don't subtract 'cur_beg' from the first entry:
    261     // * If a binary is compiled w/o -pie, then the first entry in
    262     //   process maps is likely the binary itself (all dynamic libs
    263     //   are mapped higher in address space). For such a binary,
    264     //   instruction offset in binary coincides with the actual
    265     //   instruction address in virtual memory (as code section
    266     //   is mapped to a fixed memory range).
    267     // * If a binary is compiled with -pie, all the modules are
    268     //   mapped high at address space (in particular, higher than
    269     //   shadow memory of the tool), so the module can't be the
    270     //   first entry.
    271     uptr base_address = (i ? cur_beg : 0) - cur_offset;
    272     LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
    273     cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
    274     n_modules++;
    275   }
    276   return n_modules;
    277 }
    278 
    279 void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
    280   char *smaps = 0;
    281   uptr smaps_cap = 0;
    282   uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
    283       &smaps, &smaps_cap, 64<<20);
    284   uptr start = 0;
    285   bool file = false;
    286   const char *pos = smaps;
    287   while (pos < smaps + smaps_len) {
    288     if (IsHex(pos[0])) {
    289       start = ReadHex(pos);
    290       for (; *pos != '/' && *pos > '\n'; pos++) {}
    291       file = *pos == '/';
    292     } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
    293       for (; *pos < '0' || *pos > '9'; pos++) {}
    294       uptr rss = ReadDecimal(pos) * 1024;
    295       cb(start, rss, file, stats, stats_size);
    296     }
    297     while (*pos++ != '\n') {}
    298   }
    299   UnmapOrDie(smaps, smaps_cap);
    300 }
    301 
    302 }  // namespace __sanitizer
    303 
    304 #endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
    305