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 Cygwin goes here. For the POSIX-compatible 6// parts, the implementation is in platform-posix.cc. 7 8#include <errno.h> 9#include <pthread.h> 10#include <semaphore.h> 11#include <stdarg.h> 12#include <strings.h> // index 13#include <sys/mman.h> // mmap & munmap 14#include <sys/time.h> 15#include <unistd.h> // sysconf 16 17#include <cmath> 18 19#undef MAP_TYPE 20 21#include "src/base/macros.h" 22#include "src/base/platform/platform.h" 23#include "src/base/win32-headers.h" 24 25namespace v8 { 26namespace base { 27 28 29const char* OS::LocalTimezone(double time, TimezoneCache* cache) { 30 if (std::isnan(time)) return ""; 31 time_t tv = static_cast<time_t>(std::floor(time/msPerSecond)); 32 struct tm* t = localtime(&tv); 33 if (NULL == t) return ""; 34 return tzname[0]; // The location of the timezone string on Cygwin. 35} 36 37 38double OS::LocalTimeOffset(TimezoneCache* cache) { 39 // On Cygwin, struct tm does not contain a tm_gmtoff field. 40 time_t utc = time(NULL); 41 DCHECK(utc != -1); 42 struct tm* loc = localtime(&utc); 43 DCHECK(loc != NULL); 44 // time - localtime includes any daylight savings offset, so subtract it. 45 return static_cast<double>((mktime(loc) - utc) * msPerSecond - 46 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 47} 48 49 50void* OS::Allocate(const size_t requested, 51 size_t* allocated, 52 bool is_executable) { 53 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); 54 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 55 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 56 if (mbase == MAP_FAILED) return NULL; 57 *allocated = msize; 58 return mbase; 59} 60 61 62class PosixMemoryMappedFile : public OS::MemoryMappedFile { 63 public: 64 PosixMemoryMappedFile(FILE* file, void* memory, int size) 65 : file_(file), memory_(memory), size_(size) { } 66 virtual ~PosixMemoryMappedFile(); 67 virtual void* memory() { return memory_; } 68 virtual int size() { return size_; } 69 private: 70 FILE* file_; 71 void* memory_; 72 int size_; 73}; 74 75 76OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 77 FILE* file = fopen(name, "r+"); 78 if (file == NULL) return NULL; 79 80 fseek(file, 0, SEEK_END); 81 int size = ftell(file); 82 83 void* memory = 84 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 85 return new PosixMemoryMappedFile(file, memory, size); 86} 87 88 89OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 90 void* initial) { 91 FILE* file = fopen(name, "w+"); 92 if (file == NULL) return NULL; 93 int result = fwrite(initial, size, 1, file); 94 if (result < 1) { 95 fclose(file); 96 return NULL; 97 } 98 void* memory = 99 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 100 return new PosixMemoryMappedFile(file, memory, size); 101} 102 103 104PosixMemoryMappedFile::~PosixMemoryMappedFile() { 105 if (memory_) munmap(memory_, size_); 106 fclose(file_); 107} 108 109 110std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() { 111 std::vector<SharedLibraryAddresses> result; 112 // This function assumes that the layout of the file is as follows: 113 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 114 // If we encounter an unexpected situation we abort scanning further entries. 115 FILE* fp = fopen("/proc/self/maps", "r"); 116 if (fp == NULL) return result; 117 118 // Allocate enough room to be able to store a full file name. 119 const int kLibNameLen = FILENAME_MAX + 1; 120 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 121 122 // This loop will terminate once the scanning hits an EOF. 123 while (true) { 124 uintptr_t start, end; 125 char attr_r, attr_w, attr_x, attr_p; 126 // Parse the addresses and permission bits at the beginning of the line. 127 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 128 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 129 130 int c; 131 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { 132 // Found a read-only executable entry. Skip characters until we reach 133 // the beginning of the filename or the end of the line. 134 do { 135 c = getc(fp); 136 } while ((c != EOF) && (c != '\n') && (c != '/')); 137 if (c == EOF) break; // EOF: Was unexpected, just exit. 138 139 // Process the filename if found. 140 if (c == '/') { 141 ungetc(c, fp); // Push the '/' back into the stream to be read below. 142 143 // Read to the end of the line. Exit if the read fails. 144 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 145 146 // Drop the newline character read by fgets. We do not need to check 147 // for a zero-length string because we know that we at least read the 148 // '/' character. 149 lib_name[strlen(lib_name) - 1] = '\0'; 150 } else { 151 // No library name found, just record the raw address range. 152 snprintf(lib_name, kLibNameLen, 153 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 154 } 155 result.push_back(SharedLibraryAddress(lib_name, start, end)); 156 } else { 157 // Entry not describing executable data. Skip to end of line to set up 158 // reading the next entry. 159 do { 160 c = getc(fp); 161 } while ((c != EOF) && (c != '\n')); 162 if (c == EOF) break; 163 } 164 } 165 free(lib_name); 166 fclose(fp); 167 return result; 168} 169 170 171void OS::SignalCodeMovingGC() { 172 // Nothing to do on Cygwin. 173} 174 175 176// The VirtualMemory implementation is taken from platform-win32.cc. 177// The mmap-based virtual memory implementation as it is used on most posix 178// platforms does not work well because Cygwin does not support MAP_FIXED. 179// This causes VirtualMemory::Commit to not always commit the memory region 180// specified. 181 182static void* RandomizedVirtualAlloc(size_t size, int action, int protection) { 183 LPVOID base = NULL; 184 185 if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) { 186 // For exectutable pages try and randomize the allocation address 187 for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) { 188 base = VirtualAlloc(OS::GetRandomMmapAddr(), size, action, protection); 189 } 190 } 191 192 // After three attempts give up and let the OS find an address to use. 193 if (base == NULL) base = VirtualAlloc(NULL, size, action, protection); 194 195 return base; 196} 197 198 199VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 200 201 202VirtualMemory::VirtualMemory(size_t size) 203 : address_(ReserveRegion(size)), size_(size) { } 204 205 206VirtualMemory::VirtualMemory(size_t size, size_t alignment) 207 : address_(NULL), size_(0) { 208 DCHECK((alignment % OS::AllocateAlignment()) == 0); 209 size_t request_size = RoundUp(size + alignment, 210 static_cast<intptr_t>(OS::AllocateAlignment())); 211 void* address = ReserveRegion(request_size); 212 if (address == NULL) return; 213 uint8_t* base = RoundUp(static_cast<uint8_t*>(address), alignment); 214 // Try reducing the size by freeing and then reallocating a specific area. 215 bool result = ReleaseRegion(address, request_size); 216 USE(result); 217 DCHECK(result); 218 address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS); 219 if (address != NULL) { 220 request_size = size; 221 DCHECK(base == static_cast<uint8_t*>(address)); 222 } else { 223 // Resizing failed, just go with a bigger area. 224 address = ReserveRegion(request_size); 225 if (address == NULL) return; 226 } 227 address_ = address; 228 size_ = request_size; 229} 230 231 232VirtualMemory::~VirtualMemory() { 233 if (IsReserved()) { 234 bool result = ReleaseRegion(address_, size_); 235 DCHECK(result); 236 USE(result); 237 } 238} 239 240 241bool VirtualMemory::IsReserved() { 242 return address_ != NULL; 243} 244 245 246void VirtualMemory::Reset() { 247 address_ = NULL; 248 size_ = 0; 249} 250 251 252bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 253 return CommitRegion(address, size, is_executable); 254} 255 256 257bool VirtualMemory::Uncommit(void* address, size_t size) { 258 DCHECK(IsReserved()); 259 return UncommitRegion(address, size); 260} 261 262 263void* VirtualMemory::ReserveRegion(size_t size) { 264 return RandomizedVirtualAlloc(size, MEM_RESERVE, PAGE_NOACCESS); 265} 266 267 268bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { 269 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 270 if (NULL == VirtualAlloc(base, size, MEM_COMMIT, prot)) { 271 return false; 272 } 273 return true; 274} 275 276 277bool VirtualMemory::Guard(void* address) { 278 if (NULL == VirtualAlloc(address, 279 OS::CommitPageSize(), 280 MEM_COMMIT, 281 PAGE_NOACCESS)) { 282 return false; 283 } 284 return true; 285} 286 287 288bool VirtualMemory::UncommitRegion(void* base, size_t size) { 289 return VirtualFree(base, size, MEM_DECOMMIT) != 0; 290} 291 292 293bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 294 return VirtualFree(base, 0, MEM_RELEASE) != 0; 295} 296 297 298bool VirtualMemory::HasLazyCommits() { 299 // TODO(alph): implement for the platform. 300 return false; 301} 302 303} } // namespace v8::base 304