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