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 25namespace __sanitizer { 26 27// Linker initialized. 28ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_; 29StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized. 30 31static 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 54MemoryMappingLayout::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 70MemoryMappingLayout::~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 78void MemoryMappingLayout::Reset() { 79 current_ = proc_self_maps_.data; 80} 81 82// static 83void 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 99void 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. 108static 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 128static bool IsOneOf(char c, char c1, char c2) { 129 return c == c1 || c == c2; 130} 131#endif 132 133static bool IsDecimal(char c) { 134 return c >= '0' && c <= '9'; 135} 136 137static bool IsHex(char c) { 138 return (c >= '0' && c <= '9') 139 || (c >= 'a' && c <= 'f'); 140} 141 142static 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 153static 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 160bool 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(¤t_); 198 CHECK_EQ(*current_++, '-'); 199 *end = ParseHex(¤t_); 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(¤t_); 216 CHECK_EQ(*current_++, ' '); 217 ParseHex(¤t_); 218 CHECK_EQ(*current_++, ':'); 219 ParseHex(¤t_); 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 243uptr 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 279void 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