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