OLD | NEW |
| (Empty) |
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 Linux goes here. For the POSIX-compatible | |
6 // parts, the implementation is in platform-posix.cc. | |
7 | |
8 #include <pthread.h> | |
9 #include <semaphore.h> | |
10 #include <signal.h> | |
11 #include <stdlib.h> | |
12 #include <sys/prctl.h> | |
13 #include <sys/resource.h> | |
14 #include <sys/syscall.h> | |
15 #include <sys/time.h> | |
16 #include <sys/types.h> | |
17 | |
18 // Ubuntu Dapper requires memory pages to be marked as | |
19 // executable. Otherwise, OS raises an exception when executing code | |
20 // in that page. | |
21 #include <errno.h> | |
22 #include <fcntl.h> // open | |
23 #include <stdarg.h> | |
24 #include <strings.h> // index | |
25 #include <sys/mman.h> // mmap & munmap | |
26 #include <sys/stat.h> // open | |
27 #include <sys/types.h> // mmap & munmap | |
28 #include <unistd.h> // sysconf | |
29 | |
30 // GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'. | |
31 // Old versions of the C library <signal.h> didn't define the type. | |
32 #if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \ | |
33 (defined(__arm__) || defined(__aarch64__)) && \ | |
34 !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) | |
35 #include <asm/sigcontext.h> // NOLINT | |
36 #endif | |
37 | |
38 #if defined(LEAK_SANITIZER) | |
39 #include <sanitizer/lsan_interface.h> | |
40 #endif | |
41 | |
42 #include <cmath> | |
43 | |
44 #undef MAP_TYPE | |
45 | |
46 #include "src/platform.h" | |
47 #include "src/utils.h" | |
48 | |
49 | |
50 namespace v8 { | |
51 namespace internal { | |
52 | |
53 | |
54 #ifdef __arm__ | |
55 | |
56 bool OS::ArmUsingHardFloat() { | |
57 // GCC versions 4.6 and above define __ARM_PCS or __ARM_PCS_VFP to specify | |
58 // the Floating Point ABI used (PCS stands for Procedure Call Standard). | |
59 // We use these as well as a couple of other defines to statically determine | |
60 // what FP ABI used. | |
61 // GCC versions 4.4 and below don't support hard-fp. | |
62 // GCC versions 4.5 may support hard-fp without defining __ARM_PCS or | |
63 // __ARM_PCS_VFP. | |
64 | |
65 #define GCC_VERSION (__GNUC__ * 10000 \ | |
66 + __GNUC_MINOR__ * 100 \ | |
67 + __GNUC_PATCHLEVEL__) | |
68 #if GCC_VERSION >= 40600 | |
69 #if defined(__ARM_PCS_VFP) | |
70 return true; | |
71 #else | |
72 return false; | |
73 #endif | |
74 | |
75 #elif GCC_VERSION < 40500 | |
76 return false; | |
77 | |
78 #else | |
79 #if defined(__ARM_PCS_VFP) | |
80 return true; | |
81 #elif defined(__ARM_PCS) || defined(__SOFTFP__) || defined(__SOFTFP) || \ | |
82 !defined(__VFP_FP__) | |
83 return false; | |
84 #else | |
85 #error "Your version of GCC does not report the FP ABI compiled for." \ | |
86 "Please report it on this issue" \ | |
87 "http://code.google.com/p/v8/issues/detail?id=2140" | |
88 | |
89 #endif | |
90 #endif | |
91 #undef GCC_VERSION | |
92 } | |
93 | |
94 #endif // def __arm__ | |
95 | |
96 | |
97 const char* OS::LocalTimezone(double time, TimezoneCache* cache) { | |
98 if (std::isnan(time)) return ""; | |
99 time_t tv = static_cast<time_t>(std::floor(time/msPerSecond)); | |
100 struct tm* t = localtime(&tv); | |
101 if (NULL == t) return ""; | |
102 return t->tm_zone; | |
103 } | |
104 | |
105 | |
106 double OS::LocalTimeOffset(TimezoneCache* cache) { | |
107 time_t tv = time(NULL); | |
108 struct tm* t = localtime(&tv); | |
109 // tm_gmtoff includes any daylight savings offset, so subtract it. | |
110 return static_cast<double>(t->tm_gmtoff * msPerSecond - | |
111 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0)); | |
112 } | |
113 | |
114 | |
115 void* OS::Allocate(const size_t requested, | |
116 size_t* allocated, | |
117 bool is_executable) { | |
118 const size_t msize = RoundUp(requested, AllocateAlignment()); | |
119 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); | |
120 void* addr = OS::GetRandomMmapAddr(); | |
121 void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
122 if (mbase == MAP_FAILED) return NULL; | |
123 *allocated = msize; | |
124 return mbase; | |
125 } | |
126 | |
127 | |
128 class PosixMemoryMappedFile : public OS::MemoryMappedFile { | |
129 public: | |
130 PosixMemoryMappedFile(FILE* file, void* memory, int size) | |
131 : file_(file), memory_(memory), size_(size) { } | |
132 virtual ~PosixMemoryMappedFile(); | |
133 virtual void* memory() { return memory_; } | |
134 virtual int size() { return size_; } | |
135 private: | |
136 FILE* file_; | |
137 void* memory_; | |
138 int size_; | |
139 }; | |
140 | |
141 | |
142 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { | |
143 FILE* file = fopen(name, "r+"); | |
144 if (file == NULL) return NULL; | |
145 | |
146 fseek(file, 0, SEEK_END); | |
147 int size = ftell(file); | |
148 | |
149 void* memory = | |
150 mmap(OS::GetRandomMmapAddr(), | |
151 size, | |
152 PROT_READ | PROT_WRITE, | |
153 MAP_SHARED, | |
154 fileno(file), | |
155 0); | |
156 return new PosixMemoryMappedFile(file, memory, size); | |
157 } | |
158 | |
159 | |
160 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, | |
161 void* initial) { | |
162 FILE* file = fopen(name, "w+"); | |
163 if (file == NULL) return NULL; | |
164 int result = fwrite(initial, size, 1, file); | |
165 if (result < 1) { | |
166 fclose(file); | |
167 return NULL; | |
168 } | |
169 void* memory = | |
170 mmap(OS::GetRandomMmapAddr(), | |
171 size, | |
172 PROT_READ | PROT_WRITE, | |
173 MAP_SHARED, | |
174 fileno(file), | |
175 0); | |
176 return new PosixMemoryMappedFile(file, memory, size); | |
177 } | |
178 | |
179 | |
180 PosixMemoryMappedFile::~PosixMemoryMappedFile() { | |
181 if (memory_) OS::Free(memory_, size_); | |
182 fclose(file_); | |
183 } | |
184 | |
185 | |
186 std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() { | |
187 std::vector<SharedLibraryAddress> result; | |
188 // This function assumes that the layout of the file is as follows: | |
189 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] | |
190 // If we encounter an unexpected situation we abort scanning further entries. | |
191 FILE* fp = fopen("/proc/self/maps", "r"); | |
192 if (fp == NULL) return result; | |
193 | |
194 // Allocate enough room to be able to store a full file name. | |
195 const int kLibNameLen = FILENAME_MAX + 1; | |
196 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); | |
197 | |
198 // This loop will terminate once the scanning hits an EOF. | |
199 while (true) { | |
200 uintptr_t start, end; | |
201 char attr_r, attr_w, attr_x, attr_p; | |
202 // Parse the addresses and permission bits at the beginning of the line. | |
203 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; | |
204 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; | |
205 | |
206 int c; | |
207 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { | |
208 // Found a read-only executable entry. Skip characters until we reach | |
209 // the beginning of the filename or the end of the line. | |
210 do { | |
211 c = getc(fp); | |
212 } while ((c != EOF) && (c != '\n') && (c != '/') && (c != '[')); | |
213 if (c == EOF) break; // EOF: Was unexpected, just exit. | |
214 | |
215 // Process the filename if found. | |
216 if ((c == '/') || (c == '[')) { | |
217 // Push the '/' or '[' back into the stream to be read below. | |
218 ungetc(c, fp); | |
219 | |
220 // Read to the end of the line. Exit if the read fails. | |
221 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; | |
222 | |
223 // Drop the newline character read by fgets. We do not need to check | |
224 // for a zero-length string because we know that we at least read the | |
225 // '/' or '[' character. | |
226 lib_name[strlen(lib_name) - 1] = '\0'; | |
227 } else { | |
228 // No library name found, just record the raw address range. | |
229 snprintf(lib_name, kLibNameLen, | |
230 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); | |
231 } | |
232 result.push_back(SharedLibraryAddress(lib_name, start, end)); | |
233 } else { | |
234 // Entry not describing executable data. Skip to end of line to set up | |
235 // reading the next entry. | |
236 do { | |
237 c = getc(fp); | |
238 } while ((c != EOF) && (c != '\n')); | |
239 if (c == EOF) break; | |
240 } | |
241 } | |
242 free(lib_name); | |
243 fclose(fp); | |
244 return result; | |
245 } | |
246 | |
247 | |
248 void OS::SignalCodeMovingGC() { | |
249 // Support for ll_prof.py. | |
250 // | |
251 // The Linux profiler built into the kernel logs all mmap's with | |
252 // PROT_EXEC so that analysis tools can properly attribute ticks. We | |
253 // do a mmap with a name known by ll_prof.py and immediately munmap | |
254 // it. This injects a GC marker into the stream of events generated | |
255 // by the kernel and allows us to synchronize V8 code log and the | |
256 // kernel log. | |
257 int size = sysconf(_SC_PAGESIZE); | |
258 FILE* f = fopen(OS::GetGCFakeMMapFile(), "w+"); | |
259 if (f == NULL) { | |
260 OS::PrintError("Failed to open %s\n", OS::GetGCFakeMMapFile()); | |
261 OS::Abort(); | |
262 } | |
263 void* addr = mmap(OS::GetRandomMmapAddr(), | |
264 size, | |
265 #if defined(__native_client__) | |
266 // The Native Client port of V8 uses an interpreter, | |
267 // so code pages don't need PROT_EXEC. | |
268 PROT_READ, | |
269 #else | |
270 PROT_READ | PROT_EXEC, | |
271 #endif | |
272 MAP_PRIVATE, | |
273 fileno(f), | |
274 0); | |
275 ASSERT(addr != MAP_FAILED); | |
276 OS::Free(addr, size); | |
277 fclose(f); | |
278 } | |
279 | |
280 | |
281 // Constants used for mmap. | |
282 static const int kMmapFd = -1; | |
283 static const int kMmapFdOffset = 0; | |
284 | |
285 | |
286 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } | |
287 | |
288 | |
289 VirtualMemory::VirtualMemory(size_t size) | |
290 : address_(ReserveRegion(size)), size_(size) { } | |
291 | |
292 | |
293 VirtualMemory::VirtualMemory(size_t size, size_t alignment) | |
294 : address_(NULL), size_(0) { | |
295 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); | |
296 size_t request_size = RoundUp(size + alignment, | |
297 static_cast<intptr_t>(OS::AllocateAlignment())); | |
298 void* reservation = mmap(OS::GetRandomMmapAddr(), | |
299 request_size, | |
300 PROT_NONE, | |
301 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, | |
302 kMmapFd, | |
303 kMmapFdOffset); | |
304 if (reservation == MAP_FAILED) return; | |
305 | |
306 uint8_t* base = static_cast<uint8_t*>(reservation); | |
307 uint8_t* aligned_base = RoundUp(base, alignment); | |
308 ASSERT_LE(base, aligned_base); | |
309 | |
310 // Unmap extra memory reserved before and after the desired block. | |
311 if (aligned_base != base) { | |
312 size_t prefix_size = static_cast<size_t>(aligned_base - base); | |
313 OS::Free(base, prefix_size); | |
314 request_size -= prefix_size; | |
315 } | |
316 | |
317 size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); | |
318 ASSERT_LE(aligned_size, request_size); | |
319 | |
320 if (aligned_size != request_size) { | |
321 size_t suffix_size = request_size - aligned_size; | |
322 OS::Free(aligned_base + aligned_size, suffix_size); | |
323 request_size -= suffix_size; | |
324 } | |
325 | |
326 ASSERT(aligned_size == request_size); | |
327 | |
328 address_ = static_cast<void*>(aligned_base); | |
329 size_ = aligned_size; | |
330 #if defined(LEAK_SANITIZER) | |
331 __lsan_register_root_region(address_, size_); | |
332 #endif | |
333 } | |
334 | |
335 | |
336 VirtualMemory::~VirtualMemory() { | |
337 if (IsReserved()) { | |
338 bool result = ReleaseRegion(address(), size()); | |
339 ASSERT(result); | |
340 USE(result); | |
341 } | |
342 } | |
343 | |
344 | |
345 bool VirtualMemory::IsReserved() { | |
346 return address_ != NULL; | |
347 } | |
348 | |
349 | |
350 void VirtualMemory::Reset() { | |
351 address_ = NULL; | |
352 size_ = 0; | |
353 } | |
354 | |
355 | |
356 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { | |
357 return CommitRegion(address, size, is_executable); | |
358 } | |
359 | |
360 | |
361 bool VirtualMemory::Uncommit(void* address, size_t size) { | |
362 return UncommitRegion(address, size); | |
363 } | |
364 | |
365 | |
366 bool VirtualMemory::Guard(void* address) { | |
367 OS::Guard(address, OS::CommitPageSize()); | |
368 return true; | |
369 } | |
370 | |
371 | |
372 void* VirtualMemory::ReserveRegion(size_t size) { | |
373 void* result = mmap(OS::GetRandomMmapAddr(), | |
374 size, | |
375 PROT_NONE, | |
376 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, | |
377 kMmapFd, | |
378 kMmapFdOffset); | |
379 | |
380 if (result == MAP_FAILED) return NULL; | |
381 | |
382 #if defined(LEAK_SANITIZER) | |
383 __lsan_register_root_region(result, size); | |
384 #endif | |
385 return result; | |
386 } | |
387 | |
388 | |
389 bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { | |
390 #if defined(__native_client__) | |
391 // The Native Client port of V8 uses an interpreter, | |
392 // so code pages don't need PROT_EXEC. | |
393 int prot = PROT_READ | PROT_WRITE; | |
394 #else | |
395 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); | |
396 #endif | |
397 if (MAP_FAILED == mmap(base, | |
398 size, | |
399 prot, | |
400 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, | |
401 kMmapFd, | |
402 kMmapFdOffset)) { | |
403 return false; | |
404 } | |
405 | |
406 return true; | |
407 } | |
408 | |
409 | |
410 bool VirtualMemory::UncommitRegion(void* base, size_t size) { | |
411 return mmap(base, | |
412 size, | |
413 PROT_NONE, | |
414 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, | |
415 kMmapFd, | |
416 kMmapFdOffset) != MAP_FAILED; | |
417 } | |
418 | |
419 | |
420 bool VirtualMemory::ReleaseRegion(void* base, size_t size) { | |
421 #if defined(LEAK_SANITIZER) | |
422 __lsan_unregister_root_region(base, size); | |
423 #endif | |
424 return munmap(base, size) == 0; | |
425 } | |
426 | |
427 | |
428 bool VirtualMemory::HasLazyCommits() { | |
429 return true; | |
430 } | |
431 | |
432 } } // namespace v8::internal | |
OLD | NEW |