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 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
RandomizedVirtualAlloc(size_t size,DWORD flags,DWORD protect,void * hint)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
68 class CygwinTimezoneCache : public PosixTimezoneCache {
69 const char* LocalTimezone(double time) override;
70
71 double LocalTimeOffset(double time_ms, bool is_utc) override;
72
~CygwinTimezoneCache()73 ~CygwinTimezoneCache() override {}
74 };
75
LocalTimezone(double time)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
LocalTimeOffset(double time_ms,bool is_utc)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
Allocate(void * address,size_t size,size_t alignment,MemoryPermission access)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
Free(void * address,const size_t size)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
Release(void * address,size_t size)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
SetPermissions(void * address,size_t size,MemoryPermission access)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
HasLazyCommits()175 bool OS::HasLazyCommits() {
176 // TODO(alph): implement for the platform.
177 return false;
178 }
179
GetSharedLibraryAddresses()180 std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
181 std::vector<SharedLibraryAddresses> result;
182 // This function assumes that the layout of the file is as follows:
183 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
184 // If we encounter an unexpected situation we abort scanning further entries.
185 FILE* fp = fopen("/proc/self/maps", "r");
186 if (fp == nullptr) return result;
187
188 // Allocate enough room to be able to store a full file name.
189 const int kLibNameLen = FILENAME_MAX + 1;
190 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
191
192 // This loop will terminate once the scanning hits an EOF.
193 while (true) {
194 uintptr_t start, end;
195 char attr_r, attr_w, attr_x, attr_p;
196 // Parse the addresses and permission bits at the beginning of the line.
197 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
198 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
199
200 int c;
201 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
202 // Found a read-only executable entry. Skip characters until we reach
203 // the beginning of the filename or the end of the line.
204 do {
205 c = getc(fp);
206 } while ((c != EOF) && (c != '\n') && (c != '/'));
207 if (c == EOF) break; // EOF: Was unexpected, just exit.
208
209 // Process the filename if found.
210 if (c == '/') {
211 ungetc(c, fp); // Push the '/' back into the stream to be read below.
212
213 // Read to the end of the line. Exit if the read fails.
214 if (fgets(lib_name, kLibNameLen, fp) == nullptr) break;
215
216 // Drop the newline character read by fgets. We do not need to check
217 // for a zero-length string because we know that we at least read the
218 // '/' character.
219 lib_name[strlen(lib_name) - 1] = '\0';
220 } else {
221 // No library name found, just record the raw address range.
222 snprintf(lib_name, kLibNameLen,
223 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
224 }
225 result.push_back(SharedLibraryAddress(lib_name, start, end));
226 } else {
227 // Entry not describing executable data. Skip to end of line to set up
228 // reading the next entry.
229 do {
230 c = getc(fp);
231 } while ((c != EOF) && (c != '\n'));
232 if (c == EOF) break;
233 }
234 }
235 free(lib_name);
236 fclose(fp);
237 return result;
238 }
239
SignalCodeMovingGC()240 void OS::SignalCodeMovingGC() {
241 // Nothing to do on Cygwin.
242 }
243
244 } // namespace base
245 } // namespace v8
246