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-posix.h"
23 #include "src/base/platform/platform.h"
24 #include "src/base/win32-headers.h"
25 
26 namespace v8 {
27 namespace base {
28 
29 namespace {
30 
31 // The memory allocation implementation is taken from platform-win32.cc.
32 
GetProtectionFromMemoryPermission(OS::MemoryPermission access)33 DWORD GetProtectionFromMemoryPermission(OS::MemoryPermission access) {
34   switch (access) {
35     case OS::MemoryPermission::kNoAccess:
36     case OS::MemoryPermission::kNoAccessWillJitLater:
37       return PAGE_NOACCESS;
38     case OS::MemoryPermission::kRead:
39       return PAGE_READONLY;
40     case OS::MemoryPermission::kReadWrite:
41       return PAGE_READWRITE;
42     case OS::MemoryPermission::kReadWriteExecute:
43       return PAGE_EXECUTE_READWRITE;
44     case OS::MemoryPermission::kReadExecute:
45       return PAGE_EXECUTE_READ;
46   }
47   UNREACHABLE();
48 }
49 
RandomizedVirtualAlloc(size_t size, DWORD flags, DWORD protect, void* hint)50 uint8_t* RandomizedVirtualAlloc(size_t size, DWORD flags, DWORD protect,
51                                 void* hint) {
52   LPVOID base = nullptr;
53 
54   // For executable or reserved pages try to use the address hint.
55   if (protect != PAGE_READWRITE) {
56     base = VirtualAlloc(hint, size, flags, protect);
57   }
58 
59   // If that fails, let the OS find an address to use.
60   if (base == nullptr) {
61     base = VirtualAlloc(nullptr, size, flags, protect);
62   }
63 
64   return reinterpret_cast<uint8_t*>(base);
65 }
66 
67 }  // namespace
68 
69 class CygwinTimezoneCache : public PosixTimezoneCache {
70   const char* LocalTimezone(double time) override;
71 
72   double LocalTimeOffset(double time_ms, bool is_utc) override;
73 
74   ~CygwinTimezoneCache() override {}
75 };
76 
LocalTimezone(double time)77 const char* CygwinTimezoneCache::LocalTimezone(double time) {
78   if (std::isnan(time)) return "";
79   time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
80   struct tm tm;
81   struct tm* t = localtime_r(&tv, &tm);
82   if (nullptr == t) return "";
83   return tzname[0];  // The location of the timezone string on Cygwin.
84 }
85 
LocalTimeOffset(double time_ms, bool is_utc)86 double LocalTimeOffset(double time_ms, bool is_utc) {
87   // On Cygwin, struct tm does not contain a tm_gmtoff field.
88   time_t utc = time(nullptr);
89   DCHECK_NE(utc, -1);
90   struct tm tm;
91   struct tm* loc = localtime_r(&utc, &tm);
92   DCHECK_NOT_NULL(loc);
93   // time - localtime includes any daylight savings offset, so subtract it.
94   return static_cast<double>((mktime(loc) - utc) * msPerSecond -
95                              (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
96 }
97 
98 // static
Allocate(void* hint, size_t size, size_t alignment, MemoryPermission access)99 void* OS::Allocate(void* hint, size_t size, size_t alignment,
100                    MemoryPermission access) {
101   size_t page_size = AllocatePageSize();
102   DCHECK_EQ(0, size % page_size);
103   DCHECK_EQ(0, alignment % page_size);
104   DCHECK_LE(page_size, alignment);
105   hint = AlignedAddress(hint, alignment);
106 
107   DWORD flags = (access == OS::MemoryPermission::kNoAccess)
108                     ? MEM_RESERVE
109                     : MEM_RESERVE | MEM_COMMIT;
110   DWORD protect = GetProtectionFromMemoryPermission(access);
111 
112   // First, try an exact size aligned allocation.
113   uint8_t* base = RandomizedVirtualAlloc(size, flags, protect, hint);
114   if (base == nullptr) return nullptr;  // Can't allocate, we're OOM.
115 
116   // If address is suitably aligned, we're done.
117   uint8_t* aligned_base = RoundUp(base, alignment);
118   if (base == aligned_base) return reinterpret_cast<void*>(base);
119 
120   // Otherwise, free it and try a larger allocation.
121   Free(base, size);
122 
123   // Clear the hint. It's unlikely we can allocate at this address.
124   hint = nullptr;
125 
126   // Add the maximum misalignment so we are guaranteed an aligned base address
127   // in the allocated region.
128   size_t padded_size = size + (alignment - page_size);
129   const int kMaxAttempts = 3;
130   aligned_base = nullptr;
131   for (int i = 0; i < kMaxAttempts; ++i) {
132     base = RandomizedVirtualAlloc(padded_size, flags, protect, hint);
133     if (base == nullptr) return nullptr;  // Can't allocate, we're OOM.
134 
135     // Try to trim the allocation by freeing the padded allocation and then
136     // calling VirtualAlloc at the aligned base.
137     Free(base, padded_size);
138     aligned_base = RoundUp(base, alignment);
139     base = reinterpret_cast<uint8_t*>(
140         VirtualAlloc(aligned_base, size, flags, protect));
141     // We might not get the reduced allocation due to a race. In that case,
142     // base will be nullptr.
143     if (base != nullptr) break;
144   }
145   DCHECK_IMPLIES(base, base == aligned_base);
146   return reinterpret_cast<void*>(base);
147 }
148 
149 // static
Free(void* address, const size_t size)150 void OS::Free(void* address, const size_t size) {
151   DCHECK_EQ(0, static_cast<uintptr_t>(address) % AllocatePageSize());
152   DCHECK_EQ(0, size % AllocatePageSize());
153   USE(size);
154   CHECK_NE(0, VirtualFree(address, 0, MEM_RELEASE));
155 }
156 
157 // static
Release(void* address, size_t size)158 void OS::Release(void* address, size_t size) {
159   DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
160   DCHECK_EQ(0, size % CommitPageSize());
161   CHECK_NE(0, VirtualFree(address, size, MEM_DECOMMIT));
162 }
163 
164 // static
SetPermissions(void* address, size_t size, MemoryPermission access)165 bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
166   DCHECK_EQ(0, reinterpret_cast<uintptr_t>(address) % CommitPageSize());
167   DCHECK_EQ(0, size % CommitPageSize());
168   if (access == MemoryPermission::kNoAccess) {
169     return VirtualFree(address, size, MEM_DECOMMIT) != 0;
170   }
171   DWORD protect = GetProtectionFromMemoryPermission(access);
172   return VirtualAlloc(address, size, MEM_COMMIT, protect) != nullptr;
173 }
174 
175 // static
DiscardSystemPages(void* address, size_t size)176 bool OS::DiscardSystemPages(void* address, size_t size) {
177   // On Windows, discarded pages are not returned to the system immediately and
178   // not guaranteed to be zeroed when returned to the application.
179   using DiscardVirtualMemoryFunction =
180       DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size);
181   static std::atomic<DiscardVirtualMemoryFunction> discard_virtual_memory(
182       reinterpret_cast<DiscardVirtualMemoryFunction>(-1));
183   if (discard_virtual_memory ==
184       reinterpret_cast<DiscardVirtualMemoryFunction>(-1))
185     discard_virtual_memory =
186         reinterpret_cast<DiscardVirtualMemoryFunction>(GetProcAddress(
187             GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory"));
188   // Use DiscardVirtualMemory when available because it releases faster than
189   // MEM_RESET.
190   DiscardVirtualMemoryFunction discard_function = discard_virtual_memory.load();
191   if (discard_function) {
192     DWORD ret = discard_function(address, size);
193     if (!ret) return true;
194   }
195   // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on
196   // failure.
197   void* ptr = VirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE);
198   CHECK(ptr);
199   return ptr;
200 }
201 
202 // static
HasLazyCommits()203 bool OS::HasLazyCommits() {
204   // TODO(alph): implement for the platform.
205   return false;
206 }
207 
GetSharedLibraryAddresses()208 std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
209   std::vector<SharedLibraryAddresses> result;
210   // This function assumes that the layout of the file is as follows:
211   // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
212   // If we encounter an unexpected situation we abort scanning further entries.
213   FILE* fp = fopen("/proc/self/maps", "r");
214   if (fp == nullptr) return result;
215 
216   // Allocate enough room to be able to store a full file name.
217   const int kLibNameLen = FILENAME_MAX + 1;
218   char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
219 
220   // This loop will terminate once the scanning hits an EOF.
221   while (true) {
222     uintptr_t start, end;
223     char attr_r, attr_w, attr_x, attr_p;
224     // Parse the addresses and permission bits at the beginning of the line.
225     if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
226     if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
227 
228     int c;
229     if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
230       // Found a read-only executable entry. Skip characters until we reach
231       // the beginning of the filename or the end of the line.
232       do {
233         c = getc(fp);
234       } while ((c != EOF) && (c != '\n') && (c != '/'));
235       if (c == EOF) break;  // EOF: Was unexpected, just exit.
236 
237       // Process the filename if found.
238       if (c == '/') {
239         ungetc(c, fp);  // Push the '/' back into the stream to be read below.
240 
241         // Read to the end of the line. Exit if the read fails.
242         if (fgets(lib_name, kLibNameLen, fp) == nullptr) break;
243 
244         // Drop the newline character read by fgets. We do not need to check
245         // for a zero-length string because we know that we at least read the
246         // '/' character.
247         lib_name[strlen(lib_name) - 1] = '\0';
248       } else {
249         // No library name found, just record the raw address range.
250         snprintf(lib_name, kLibNameLen,
251                  "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
252       }
253       result.push_back(SharedLibraryAddress(lib_name, start, end));
254     } else {
255       // Entry not describing executable data. Skip to end of line to set up
256       // reading the next entry.
257       do {
258         c = getc(fp);
259       } while ((c != EOF) && (c != '\n'));
260       if (c == EOF) break;
261     }
262   }
263   free(lib_name);
264   fclose(fp);
265   return result;
266 }
267 
SignalCodeMovingGC()268 void OS::SignalCodeMovingGC() {
269   // Nothing to do on Cygwin.
270 }
271 
AdjustSchedulingParams()272 void OS::AdjustSchedulingParams() {}
273 
GetFreeMemoryRangesWithin( OS::Address boundary_start, OS::Address boundary_end, size_t minimum_size, size_t alignment)274 std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
275     OS::Address boundary_start, OS::Address boundary_end, size_t minimum_size,
276     size_t alignment) {
277   return {};
278 }
279 
280 }  // namespace base
281 }  // namespace v8
282