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