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.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