1// Copyright 2012 the V8 project 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// Platform-specific code for OpenBSD and NetBSD goes here. For the 6// POSIX-compatible parts, the implementation is in platform-posix.cc. 7 8#include <pthread.h> 9#include <semaphore.h> 10#include <signal.h> 11#include <stdlib.h> 12#include <sys/resource.h> 13#include <sys/syscall.h> 14#include <sys/time.h> 15#include <sys/types.h> 16 17#include <errno.h> 18#include <fcntl.h> // open 19#include <stdarg.h> 20#include <strings.h> // index 21#include <sys/mman.h> // mmap & munmap 22#include <sys/stat.h> // open 23#include <sys/types.h> // mmap & munmap 24#include <unistd.h> // sysconf 25 26#include <cmath> 27 28#undef MAP_TYPE 29 30#include "src/base/macros.h" 31#include "src/base/platform/platform.h" 32 33 34namespace v8 { 35namespace base { 36 37 38const char* OS::LocalTimezone(double time, TimezoneCache* cache) { 39 if (std::isnan(time)) return ""; 40 time_t tv = static_cast<time_t>(std::floor(time/msPerSecond)); 41 struct tm* t = localtime(&tv); 42 if (NULL == t) return ""; 43 return t->tm_zone; 44} 45 46 47double OS::LocalTimeOffset(TimezoneCache* cache) { 48 time_t tv = time(NULL); 49 struct tm* t = localtime(&tv); 50 // tm_gmtoff includes any daylight savings offset, so subtract it. 51 return static_cast<double>(t->tm_gmtoff * msPerSecond - 52 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 53} 54 55 56void* OS::Allocate(const size_t requested, 57 size_t* allocated, 58 bool is_executable) { 59 const size_t msize = RoundUp(requested, AllocateAlignment()); 60 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 61 void* addr = OS::GetRandomMmapAddr(); 62 void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); 63 if (mbase == MAP_FAILED) return NULL; 64 *allocated = msize; 65 return mbase; 66} 67 68 69class PosixMemoryMappedFile : public OS::MemoryMappedFile { 70 public: 71 PosixMemoryMappedFile(FILE* file, void* memory, int size) 72 : file_(file), memory_(memory), size_(size) { } 73 virtual ~PosixMemoryMappedFile(); 74 virtual void* memory() { return memory_; } 75 virtual int size() { return size_; } 76 private: 77 FILE* file_; 78 void* memory_; 79 int size_; 80}; 81 82 83OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 84 FILE* file = fopen(name, "r+"); 85 if (file == NULL) return NULL; 86 87 fseek(file, 0, SEEK_END); 88 int size = ftell(file); 89 90 void* memory = 91 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 92 return new PosixMemoryMappedFile(file, memory, size); 93} 94 95 96OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 97 void* initial) { 98 FILE* file = fopen(name, "w+"); 99 if (file == NULL) return NULL; 100 int result = fwrite(initial, size, 1, file); 101 if (result < 1) { 102 fclose(file); 103 return NULL; 104 } 105 void* memory = 106 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 107 return new PosixMemoryMappedFile(file, memory, size); 108} 109 110 111PosixMemoryMappedFile::~PosixMemoryMappedFile() { 112 if (memory_) OS::Free(memory_, size_); 113 fclose(file_); 114} 115 116 117std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() { 118 std::vector<SharedLibraryAddress> result; 119 // This function assumes that the layout of the file is as follows: 120 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 121 // If we encounter an unexpected situation we abort scanning further entries. 122 FILE* fp = fopen("/proc/self/maps", "r"); 123 if (fp == NULL) return result; 124 125 // Allocate enough room to be able to store a full file name. 126 const int kLibNameLen = FILENAME_MAX + 1; 127 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 128 129 // This loop will terminate once the scanning hits an EOF. 130 while (true) { 131 uintptr_t start, end; 132 char attr_r, attr_w, attr_x, attr_p; 133 // Parse the addresses and permission bits at the beginning of the line. 134 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 135 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 136 137 int c; 138 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { 139 // Found a read-only executable entry. Skip characters until we reach 140 // the beginning of the filename or the end of the line. 141 do { 142 c = getc(fp); 143 } while ((c != EOF) && (c != '\n') && (c != '/')); 144 if (c == EOF) break; // EOF: Was unexpected, just exit. 145 146 // Process the filename if found. 147 if (c == '/') { 148 ungetc(c, fp); // Push the '/' back into the stream to be read below. 149 150 // Read to the end of the line. Exit if the read fails. 151 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 152 153 // Drop the newline character read by fgets. We do not need to check 154 // for a zero-length string because we know that we at least read the 155 // '/' character. 156 lib_name[strlen(lib_name) - 1] = '\0'; 157 } else { 158 // No library name found, just record the raw address range. 159 snprintf(lib_name, kLibNameLen, 160 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 161 } 162 result.push_back(SharedLibraryAddress(lib_name, start, end)); 163 } else { 164 // Entry not describing executable data. Skip to end of line to set up 165 // reading the next entry. 166 do { 167 c = getc(fp); 168 } while ((c != EOF) && (c != '\n')); 169 if (c == EOF) break; 170 } 171 } 172 free(lib_name); 173 fclose(fp); 174 return result; 175} 176 177 178void OS::SignalCodeMovingGC() { 179 // Support for ll_prof.py. 180 // 181 // The Linux profiler built into the kernel logs all mmap's with 182 // PROT_EXEC so that analysis tools can properly attribute ticks. We 183 // do a mmap with a name known by ll_prof.py and immediately munmap 184 // it. This injects a GC marker into the stream of events generated 185 // by the kernel and allows us to synchronize V8 code log and the 186 // kernel log. 187 int size = sysconf(_SC_PAGESIZE); 188 FILE* f = fopen(OS::GetGCFakeMMapFile(), "w+"); 189 if (f == NULL) { 190 OS::PrintError("Failed to open %s\n", OS::GetGCFakeMMapFile()); 191 OS::Abort(); 192 } 193 void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, 194 fileno(f), 0); 195 DCHECK(addr != MAP_FAILED); 196 OS::Free(addr, size); 197 fclose(f); 198} 199 200 201 202// Constants used for mmap. 203static const int kMmapFd = -1; 204static const int kMmapFdOffset = 0; 205 206 207VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 208 209 210VirtualMemory::VirtualMemory(size_t size) 211 : address_(ReserveRegion(size)), size_(size) { } 212 213 214VirtualMemory::VirtualMemory(size_t size, size_t alignment) 215 : address_(NULL), size_(0) { 216 DCHECK((alignment % OS::AllocateAlignment()) == 0); 217 size_t request_size = RoundUp(size + alignment, 218 static_cast<intptr_t>(OS::AllocateAlignment())); 219 void* reservation = mmap(OS::GetRandomMmapAddr(), 220 request_size, 221 PROT_NONE, 222 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, 223 kMmapFd, 224 kMmapFdOffset); 225 if (reservation == MAP_FAILED) return; 226 227 uint8_t* base = static_cast<uint8_t*>(reservation); 228 uint8_t* aligned_base = RoundUp(base, alignment); 229 DCHECK_LE(base, aligned_base); 230 231 // Unmap extra memory reserved before and after the desired block. 232 if (aligned_base != base) { 233 size_t prefix_size = static_cast<size_t>(aligned_base - base); 234 OS::Free(base, prefix_size); 235 request_size -= prefix_size; 236 } 237 238 size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); 239 DCHECK_LE(aligned_size, request_size); 240 241 if (aligned_size != request_size) { 242 size_t suffix_size = request_size - aligned_size; 243 OS::Free(aligned_base + aligned_size, suffix_size); 244 request_size -= suffix_size; 245 } 246 247 DCHECK(aligned_size == request_size); 248 249 address_ = static_cast<void*>(aligned_base); 250 size_ = aligned_size; 251} 252 253 254VirtualMemory::~VirtualMemory() { 255 if (IsReserved()) { 256 bool result = ReleaseRegion(address(), size()); 257 DCHECK(result); 258 USE(result); 259 } 260} 261 262 263bool VirtualMemory::IsReserved() { 264 return address_ != NULL; 265} 266 267 268void VirtualMemory::Reset() { 269 address_ = NULL; 270 size_ = 0; 271} 272 273 274bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 275 return CommitRegion(address, size, is_executable); 276} 277 278 279bool VirtualMemory::Uncommit(void* address, size_t size) { 280 return UncommitRegion(address, size); 281} 282 283 284bool VirtualMemory::Guard(void* address) { 285 OS::Guard(address, OS::CommitPageSize()); 286 return true; 287} 288 289 290void* VirtualMemory::ReserveRegion(size_t size) { 291 void* result = mmap(OS::GetRandomMmapAddr(), 292 size, 293 PROT_NONE, 294 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, 295 kMmapFd, 296 kMmapFdOffset); 297 298 if (result == MAP_FAILED) return NULL; 299 300 return result; 301} 302 303 304bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { 305 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 306 if (MAP_FAILED == mmap(base, 307 size, 308 prot, 309 MAP_PRIVATE | MAP_ANON | MAP_FIXED, 310 kMmapFd, 311 kMmapFdOffset)) { 312 return false; 313 } 314 return true; 315} 316 317 318bool VirtualMemory::UncommitRegion(void* base, size_t size) { 319 return mmap(base, 320 size, 321 PROT_NONE, 322 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, 323 kMmapFd, 324 kMmapFdOffset) != MAP_FAILED; 325} 326 327 328bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 329 return munmap(base, size) == 0; 330} 331 332 333bool VirtualMemory::HasLazyCommits() { 334 // TODO(alph): implement for the platform. 335 return false; 336} 337 338} } // namespace v8::base 339