Home | History | Annotate | Download | only in src
      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(&current_time, NULL) == -1) {
    442     return false;
    443   }
    444 
    445   // Calculate time for end of timeout.
    446   struct timeval end_time;
    447   timeradd(&current_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