1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 // Platform specific code for Cygwin goes here. For the POSIX comaptible parts 29 // the implementation is in platform-posix.cc. 30 31 #include <errno.h> 32 #include <pthread.h> 33 #include <semaphore.h> 34 #include <stdarg.h> 35 #include <strings.h> // index 36 #include <sys/time.h> 37 #include <sys/mman.h> // mmap & munmap 38 #include <unistd.h> // sysconf 39 40 #undef MAP_TYPE 41 42 #include "v8.h" 43 44 #include "platform.h" 45 #include "simulator.h" 46 #include "v8threads.h" 47 #include "vm-state-inl.h" 48 #include "win32-headers.h" 49 50 namespace v8 { 51 namespace internal { 52 53 54 const char* OS::LocalTimezone(double time) { 55 if (std::isnan(time)) return ""; 56 time_t tv = static_cast<time_t>(floor(time/msPerSecond)); 57 struct tm* t = localtime(&tv); 58 if (NULL == t) return ""; 59 return tzname[0]; // The location of the timezone string on Cygwin. 60 } 61 62 63 double OS::LocalTimeOffset() { 64 // On Cygwin, struct tm does not contain a tm_gmtoff field. 65 time_t utc = time(NULL); 66 ASSERT(utc != -1); 67 struct tm* loc = localtime(&utc); 68 ASSERT(loc != NULL); 69 // time - localtime includes any daylight savings offset, so subtract it. 70 return static_cast<double>((mktime(loc) - utc) * msPerSecond - 71 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 72 } 73 74 75 void* OS::Allocate(const size_t requested, 76 size_t* allocated, 77 bool is_executable) { 78 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); 79 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 80 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 81 if (mbase == MAP_FAILED) { 82 LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed")); 83 return NULL; 84 } 85 *allocated = msize; 86 return mbase; 87 } 88 89 90 class PosixMemoryMappedFile : public OS::MemoryMappedFile { 91 public: 92 PosixMemoryMappedFile(FILE* file, void* memory, int size) 93 : file_(file), memory_(memory), size_(size) { } 94 virtual ~PosixMemoryMappedFile(); 95 virtual void* memory() { return memory_; } 96 virtual int size() { return size_; } 97 private: 98 FILE* file_; 99 void* memory_; 100 int size_; 101 }; 102 103 104 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 105 FILE* file = fopen(name, "r+"); 106 if (file == NULL) return NULL; 107 108 fseek(file, 0, SEEK_END); 109 int size = ftell(file); 110 111 void* memory = 112 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 113 return new PosixMemoryMappedFile(file, memory, size); 114 } 115 116 117 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 118 void* initial) { 119 FILE* file = fopen(name, "w+"); 120 if (file == NULL) return NULL; 121 int result = fwrite(initial, size, 1, file); 122 if (result < 1) { 123 fclose(file); 124 return NULL; 125 } 126 void* memory = 127 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 128 return new PosixMemoryMappedFile(file, memory, size); 129 } 130 131 132 PosixMemoryMappedFile::~PosixMemoryMappedFile() { 133 if (memory_) munmap(memory_, size_); 134 fclose(file_); 135 } 136 137 138 void OS::LogSharedLibraryAddresses(Isolate* isolate) { 139 // This function assumes that the layout of the file is as follows: 140 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 141 // If we encounter an unexpected situation we abort scanning further entries. 142 FILE* fp = fopen("/proc/self/maps", "r"); 143 if (fp == NULL) return; 144 145 // Allocate enough room to be able to store a full file name. 146 const int kLibNameLen = FILENAME_MAX + 1; 147 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 148 149 // This loop will terminate once the scanning hits an EOF. 150 while (true) { 151 uintptr_t start, end; 152 char attr_r, attr_w, attr_x, attr_p; 153 // Parse the addresses and permission bits at the beginning of the line. 154 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 155 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 156 157 int c; 158 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { 159 // Found a read-only executable entry. Skip characters until we reach 160 // the beginning of the filename or the end of the line. 161 do { 162 c = getc(fp); 163 } while ((c != EOF) && (c != '\n') && (c != '/')); 164 if (c == EOF) break; // EOF: Was unexpected, just exit. 165 166 // Process the filename if found. 167 if (c == '/') { 168 ungetc(c, fp); // Push the '/' back into the stream to be read below. 169 170 // Read to the end of the line. Exit if the read fails. 171 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 172 173 // Drop the newline character read by fgets. We do not need to check 174 // for a zero-length string because we know that we at least read the 175 // '/' character. 176 lib_name[strlen(lib_name) - 1] = '\0'; 177 } else { 178 // No library name found, just record the raw address range. 179 snprintf(lib_name, kLibNameLen, 180 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 181 } 182 LOG(isolate, SharedLibraryEvent(lib_name, start, end)); 183 } else { 184 // Entry not describing executable data. Skip to end of line to set up 185 // reading the next entry. 186 do { 187 c = getc(fp); 188 } while ((c != EOF) && (c != '\n')); 189 if (c == EOF) break; 190 } 191 } 192 free(lib_name); 193 fclose(fp); 194 } 195 196 197 void OS::SignalCodeMovingGC() { 198 // Nothing to do on Cygwin. 199 } 200 201 202 // The VirtualMemory implementation is taken from platform-win32.cc. 203 // The mmap-based virtual memory implementation as it is used on most posix 204 // platforms does not work well because Cygwin does not support MAP_FIXED. 205 // This causes VirtualMemory::Commit to not always commit the memory region 206 // specified. 207 208 static void* GetRandomAddr() { 209 Isolate* isolate = Isolate::UncheckedCurrent(); 210 // Note that the current isolate isn't set up in a call path via 211 // CpuFeatures::Probe. We don't care about randomization in this case because 212 // the code page is immediately freed. 213 if (isolate != NULL) { 214 // The address range used to randomize RWX allocations in OS::Allocate 215 // Try not to map pages into the default range that windows loads DLLs 216 // Use a multiple of 64k to prevent committing unused memory. 217 // Note: This does not guarantee RWX regions will be within the 218 // range kAllocationRandomAddressMin to kAllocationRandomAddressMax 219 #ifdef V8_HOST_ARCH_64_BIT 220 static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000; 221 static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000; 222 #else 223 static const intptr_t kAllocationRandomAddressMin = 0x04000000; 224 static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000; 225 #endif 226 uintptr_t address = 227 (isolate->random_number_generator()->NextInt() << kPageSizeBits) | 228 kAllocationRandomAddressMin; 229 address &= kAllocationRandomAddressMax; 230 return reinterpret_cast<void *>(address); 231 } 232 return NULL; 233 } 234 235 236 static void* RandomizedVirtualAlloc(size_t size, int action, int protection) { 237 LPVOID base = NULL; 238 239 if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) { 240 // For exectutable pages try and randomize the allocation address 241 for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) { 242 base = VirtualAlloc(GetRandomAddr(), size, action, protection); 243 } 244 } 245 246 // After three attempts give up and let the OS find an address to use. 247 if (base == NULL) base = VirtualAlloc(NULL, size, action, protection); 248 249 return base; 250 } 251 252 253 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 254 255 256 VirtualMemory::VirtualMemory(size_t size) 257 : address_(ReserveRegion(size)), size_(size) { } 258 259 260 VirtualMemory::VirtualMemory(size_t size, size_t alignment) 261 : address_(NULL), size_(0) { 262 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); 263 size_t request_size = RoundUp(size + alignment, 264 static_cast<intptr_t>(OS::AllocateAlignment())); 265 void* address = ReserveRegion(request_size); 266 if (address == NULL) return; 267 Address base = RoundUp(static_cast<Address>(address), alignment); 268 // Try reducing the size by freeing and then reallocating a specific area. 269 bool result = ReleaseRegion(address, request_size); 270 USE(result); 271 ASSERT(result); 272 address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS); 273 if (address != NULL) { 274 request_size = size; 275 ASSERT(base == static_cast<Address>(address)); 276 } else { 277 // Resizing failed, just go with a bigger area. 278 address = ReserveRegion(request_size); 279 if (address == NULL) return; 280 } 281 address_ = address; 282 size_ = request_size; 283 } 284 285 286 VirtualMemory::~VirtualMemory() { 287 if (IsReserved()) { 288 bool result = ReleaseRegion(address_, size_); 289 ASSERT(result); 290 USE(result); 291 } 292 } 293 294 295 bool VirtualMemory::IsReserved() { 296 return address_ != NULL; 297 } 298 299 300 void VirtualMemory::Reset() { 301 address_ = NULL; 302 size_ = 0; 303 } 304 305 306 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 307 return CommitRegion(address, size, is_executable); 308 } 309 310 311 bool VirtualMemory::Uncommit(void* address, size_t size) { 312 ASSERT(IsReserved()); 313 return UncommitRegion(address, size); 314 } 315 316 317 void* VirtualMemory::ReserveRegion(size_t size) { 318 return RandomizedVirtualAlloc(size, MEM_RESERVE, PAGE_NOACCESS); 319 } 320 321 322 bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { 323 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 324 if (NULL == VirtualAlloc(base, size, MEM_COMMIT, prot)) { 325 return false; 326 } 327 return true; 328 } 329 330 331 bool VirtualMemory::Guard(void* address) { 332 if (NULL == VirtualAlloc(address, 333 OS::CommitPageSize(), 334 MEM_COMMIT, 335 PAGE_NOACCESS)) { 336 return false; 337 } 338 return true; 339 } 340 341 342 bool VirtualMemory::UncommitRegion(void* base, size_t size) { 343 return VirtualFree(base, size, MEM_DECOMMIT) != 0; 344 } 345 346 347 bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 348 return VirtualFree(base, 0, MEM_RELEASE) != 0; 349 } 350 351 352 bool VirtualMemory::HasLazyCommits() { 353 // TODO(alph): implement for the platform. 354 return false; 355 } 356 357 } } // namespace v8::internal 358