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-posix.h" 45 #include "platform.h" 46 #include "simulator.h" 47 #include "v8threads.h" 48 #include "vm-state-inl.h" 49 #include "win32-headers.h" 50 51 namespace v8 { 52 namespace internal { 53 54 55 static Mutex* limit_mutex = NULL; 56 57 58 const char* OS::LocalTimezone(double time) { 59 if (std::isnan(time)) return ""; 60 time_t tv = static_cast<time_t>(floor(time/msPerSecond)); 61 struct tm* t = localtime(&tv); 62 if (NULL == t) return ""; 63 return tzname[0]; // The location of the timezone string on Cygwin. 64 } 65 66 67 double OS::LocalTimeOffset() { 68 // On Cygwin, struct tm does not contain a tm_gmtoff field. 69 time_t utc = time(NULL); 70 ASSERT(utc != -1); 71 struct tm* loc = localtime(&utc); 72 ASSERT(loc != NULL); 73 // time - localtime includes any daylight savings offset, so subtract it. 74 return static_cast<double>((mktime(loc) - utc) * msPerSecond - 75 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 76 } 77 78 79 // We keep the lowest and highest addresses mapped as a quick way of 80 // determining that pointers are outside the heap (used mostly in assertions 81 // and verification). The estimate is conservative, i.e., not all addresses in 82 // 'allocated' space are actually allocated to our heap. The range is 83 // [lowest, highest), inclusive on the low and and exclusive on the high end. 84 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 85 static void* highest_ever_allocated = reinterpret_cast<void*>(0); 86 87 88 static void UpdateAllocatedSpaceLimits(void* address, int size) { 89 ASSERT(limit_mutex != NULL); 90 ScopedLock lock(limit_mutex); 91 92 lowest_ever_allocated = Min(lowest_ever_allocated, address); 93 highest_ever_allocated = 94 Max(highest_ever_allocated, 95 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); 96 } 97 98 99 bool OS::IsOutsideAllocatedSpace(void* address) { 100 return address < lowest_ever_allocated || address >= highest_ever_allocated; 101 } 102 103 104 void* OS::Allocate(const size_t requested, 105 size_t* allocated, 106 bool is_executable) { 107 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); 108 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 109 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 110 if (mbase == MAP_FAILED) { 111 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed")); 112 return NULL; 113 } 114 *allocated = msize; 115 UpdateAllocatedSpaceLimits(mbase, msize); 116 return mbase; 117 } 118 119 120 void OS::DumpBacktrace() { 121 // Currently unsupported. 122 } 123 124 125 class PosixMemoryMappedFile : public OS::MemoryMappedFile { 126 public: 127 PosixMemoryMappedFile(FILE* file, void* memory, int size) 128 : file_(file), memory_(memory), size_(size) { } 129 virtual ~PosixMemoryMappedFile(); 130 virtual void* memory() { return memory_; } 131 virtual int size() { return size_; } 132 private: 133 FILE* file_; 134 void* memory_; 135 int size_; 136 }; 137 138 139 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 140 FILE* file = fopen(name, "r+"); 141 if (file == NULL) return NULL; 142 143 fseek(file, 0, SEEK_END); 144 int size = ftell(file); 145 146 void* memory = 147 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 148 return new PosixMemoryMappedFile(file, memory, size); 149 } 150 151 152 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 153 void* initial) { 154 FILE* file = fopen(name, "w+"); 155 if (file == NULL) return NULL; 156 int result = fwrite(initial, size, 1, file); 157 if (result < 1) { 158 fclose(file); 159 return NULL; 160 } 161 void* memory = 162 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 163 return new PosixMemoryMappedFile(file, memory, size); 164 } 165 166 167 PosixMemoryMappedFile::~PosixMemoryMappedFile() { 168 if (memory_) munmap(memory_, size_); 169 fclose(file_); 170 } 171 172 173 void OS::LogSharedLibraryAddresses() { 174 // This function assumes that the layout of the file is as follows: 175 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 176 // If we encounter an unexpected situation we abort scanning further entries. 177 FILE* fp = fopen("/proc/self/maps", "r"); 178 if (fp == NULL) return; 179 180 // Allocate enough room to be able to store a full file name. 181 const int kLibNameLen = FILENAME_MAX + 1; 182 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 183 184 i::Isolate* isolate = ISOLATE; 185 // This loop will terminate once the scanning hits an EOF. 186 while (true) { 187 uintptr_t start, end; 188 char attr_r, attr_w, attr_x, attr_p; 189 // Parse the addresses and permission bits at the beginning of the line. 190 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 191 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 192 193 int c; 194 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { 195 // Found a read-only executable entry. Skip characters until we reach 196 // the beginning of the filename or the end of the line. 197 do { 198 c = getc(fp); 199 } while ((c != EOF) && (c != '\n') && (c != '/')); 200 if (c == EOF) break; // EOF: Was unexpected, just exit. 201 202 // Process the filename if found. 203 if (c == '/') { 204 ungetc(c, fp); // Push the '/' back into the stream to be read below. 205 206 // Read to the end of the line. Exit if the read fails. 207 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 208 209 // Drop the newline character read by fgets. We do not need to check 210 // for a zero-length string because we know that we at least read the 211 // '/' character. 212 lib_name[strlen(lib_name) - 1] = '\0'; 213 } else { 214 // No library name found, just record the raw address range. 215 snprintf(lib_name, kLibNameLen, 216 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 217 } 218 LOG(isolate, SharedLibraryEvent(lib_name, start, end)); 219 } else { 220 // Entry not describing executable data. Skip to end of line to set up 221 // reading the next entry. 222 do { 223 c = getc(fp); 224 } while ((c != EOF) && (c != '\n')); 225 if (c == EOF) break; 226 } 227 } 228 free(lib_name); 229 fclose(fp); 230 } 231 232 233 void OS::SignalCodeMovingGC() { 234 // Nothing to do on Cygwin. 235 } 236 237 238 int OS::StackWalk(Vector<OS::StackFrame> frames) { 239 // Not supported on Cygwin. 240 return 0; 241 } 242 243 244 // The VirtualMemory implementation is taken from platform-win32.cc. 245 // The mmap-based virtual memory implementation as it is used on most posix 246 // platforms does not work well because Cygwin does not support MAP_FIXED. 247 // This causes VirtualMemory::Commit to not always commit the memory region 248 // specified. 249 250 static void* GetRandomAddr() { 251 Isolate* isolate = Isolate::UncheckedCurrent(); 252 // Note that the current isolate isn't set up in a call path via 253 // CpuFeatures::Probe. We don't care about randomization in this case because 254 // the code page is immediately freed. 255 if (isolate != NULL) { 256 // The address range used to randomize RWX allocations in OS::Allocate 257 // Try not to map pages into the default range that windows loads DLLs 258 // Use a multiple of 64k to prevent committing unused memory. 259 // Note: This does not guarantee RWX regions will be within the 260 // range kAllocationRandomAddressMin to kAllocationRandomAddressMax 261 #ifdef V8_HOST_ARCH_64_BIT 262 static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000; 263 static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000; 264 #else 265 static const intptr_t kAllocationRandomAddressMin = 0x04000000; 266 static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000; 267 #endif 268 uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits) 269 | kAllocationRandomAddressMin; 270 address &= kAllocationRandomAddressMax; 271 return reinterpret_cast<void *>(address); 272 } 273 return NULL; 274 } 275 276 277 static void* RandomizedVirtualAlloc(size_t size, int action, int protection) { 278 LPVOID base = NULL; 279 280 if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) { 281 // For exectutable pages try and randomize the allocation address 282 for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) { 283 base = VirtualAlloc(GetRandomAddr(), size, action, protection); 284 } 285 } 286 287 // After three attempts give up and let the OS find an address to use. 288 if (base == NULL) base = VirtualAlloc(NULL, size, action, protection); 289 290 return base; 291 } 292 293 294 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 295 296 297 VirtualMemory::VirtualMemory(size_t size) 298 : address_(ReserveRegion(size)), size_(size) { } 299 300 301 VirtualMemory::VirtualMemory(size_t size, size_t alignment) 302 : address_(NULL), size_(0) { 303 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); 304 size_t request_size = RoundUp(size + alignment, 305 static_cast<intptr_t>(OS::AllocateAlignment())); 306 void* address = ReserveRegion(request_size); 307 if (address == NULL) return; 308 Address base = RoundUp(static_cast<Address>(address), alignment); 309 // Try reducing the size by freeing and then reallocating a specific area. 310 bool result = ReleaseRegion(address, request_size); 311 USE(result); 312 ASSERT(result); 313 address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS); 314 if (address != NULL) { 315 request_size = size; 316 ASSERT(base == static_cast<Address>(address)); 317 } else { 318 // Resizing failed, just go with a bigger area. 319 address = ReserveRegion(request_size); 320 if (address == NULL) return; 321 } 322 address_ = address; 323 size_ = request_size; 324 } 325 326 327 VirtualMemory::~VirtualMemory() { 328 if (IsReserved()) { 329 bool result = ReleaseRegion(address_, size_); 330 ASSERT(result); 331 USE(result); 332 } 333 } 334 335 336 bool VirtualMemory::IsReserved() { 337 return address_ != NULL; 338 } 339 340 341 void VirtualMemory::Reset() { 342 address_ = NULL; 343 size_ = 0; 344 } 345 346 347 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 348 return CommitRegion(address, size, is_executable); 349 } 350 351 352 bool VirtualMemory::Uncommit(void* address, size_t size) { 353 ASSERT(IsReserved()); 354 return UncommitRegion(address, size); 355 } 356 357 358 void* VirtualMemory::ReserveRegion(size_t size) { 359 return RandomizedVirtualAlloc(size, MEM_RESERVE, PAGE_NOACCESS); 360 } 361 362 363 bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { 364 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 365 if (NULL == VirtualAlloc(base, size, MEM_COMMIT, prot)) { 366 return false; 367 } 368 369 UpdateAllocatedSpaceLimits(base, static_cast<int>(size)); 370 return true; 371 } 372 373 374 bool VirtualMemory::Guard(void* address) { 375 if (NULL == VirtualAlloc(address, 376 OS::CommitPageSize(), 377 MEM_COMMIT, 378 PAGE_READONLY | PAGE_GUARD)) { 379 return false; 380 } 381 return true; 382 } 383 384 385 bool VirtualMemory::UncommitRegion(void* base, size_t size) { 386 return VirtualFree(base, size, MEM_DECOMMIT) != 0; 387 } 388 389 390 bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 391 return VirtualFree(base, 0, MEM_RELEASE) != 0; 392 } 393 394 395 bool VirtualMemory::HasLazyCommits() { 396 // TODO(alph): implement for the platform. 397 return false; 398 } 399 400 401 class CygwinSemaphore : public Semaphore { 402 public: 403 explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); } 404 virtual ~CygwinSemaphore() { sem_destroy(&sem_); } 405 406 virtual void Wait(); 407 virtual bool Wait(int timeout); 408 virtual void Signal() { sem_post(&sem_); } 409 private: 410 sem_t sem_; 411 }; 412 413 414 void CygwinSemaphore::Wait() { 415 while (true) { 416 int result = sem_wait(&sem_); 417 if (result == 0) return; // Successfully got semaphore. 418 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 419 } 420 } 421 422 423 #ifndef TIMEVAL_TO_TIMESPEC 424 #define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ 425 (ts)->tv_sec = (tv)->tv_sec; \ 426 (ts)->tv_nsec = (tv)->tv_usec * 1000; \ 427 } while (false) 428 #endif 429 430 431 bool CygwinSemaphore::Wait(int timeout) { 432 const long kOneSecondMicros = 1000000; // NOLINT 433 434 // Split timeout into second and nanosecond parts. 435 struct timeval delta; 436 delta.tv_usec = timeout % kOneSecondMicros; 437 delta.tv_sec = timeout / kOneSecondMicros; 438 439 struct timeval current_time; 440 // Get the current time. 441 if (gettimeofday(¤t_time, NULL) == -1) { 442 return false; 443 } 444 445 // Calculate time for end of timeout. 446 struct timeval end_time; 447 timeradd(¤t_time, &delta, &end_time); 448 449 struct timespec ts; 450 TIMEVAL_TO_TIMESPEC(&end_time, &ts); 451 // Wait for semaphore signalled or timeout. 452 while (true) { 453 int result = sem_timedwait(&sem_, &ts); 454 if (result == 0) return true; // Successfully got semaphore. 455 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. 456 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 457 } 458 } 459 460 461 Semaphore* OS::CreateSemaphore(int count) { 462 return new CygwinSemaphore(count); 463 } 464 465 466 void OS::SetUp() { 467 // Seed the random number generator. 468 // Convert the current time to a 64-bit integer first, before converting it 469 // to an unsigned. Going directly can cause an overflow and the seed to be 470 // set to all ones. The seed will be identical for different instances that 471 // call this setup code within the same millisecond. 472 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); 473 srandom(static_cast<unsigned int>(seed)); 474 limit_mutex = CreateMutex(); 475 } 476 477 478 void OS::TearDown() { 479 delete limit_mutex; 480 } 481 482 483 } } // namespace v8::internal 484