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 POSIX goes here. This is not a platform on its
6// own, but contains the parts which are the same across the POSIX platforms
7// Linux, MacOS, FreeBSD, OpenBSD, NetBSD and QNX.
8
9#include <errno.h>
10#include <limits.h>
11#include <pthread.h>
12#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
13#include <pthread_np.h>  // for pthread_set_name_np
14#endif
15#include <sched.h>  // for sched_yield
16#include <time.h>
17#include <unistd.h>
18
19#include <sys/mman.h>
20#include <sys/resource.h>
21#include <sys/stat.h>
22#include <sys/time.h>
23#include <sys/types.h>
24#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
25    defined(__NetBSD__) || defined(__OpenBSD__)
26#include <sys/sysctl.h>  // NOLINT, for sysctl
27#endif
28
29#undef MAP_TYPE
30
31#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
32#define LOG_TAG "v8"
33#include <android/log.h>  // NOLINT
34#endif
35
36#include <cmath>
37#include <cstdlib>
38
39#include "src/base/lazy-instance.h"
40#include "src/base/macros.h"
41#include "src/base/platform/platform.h"
42#include "src/base/platform/time.h"
43#include "src/base/utils/random-number-generator.h"
44
45#ifdef V8_FAST_TLS_SUPPORTED
46#include "src/base/atomicops.h"
47#endif
48
49#if V8_OS_MACOSX
50#include <dlfcn.h>
51#endif
52
53#if V8_OS_LINUX
54#include <sys/prctl.h>  // NOLINT, for prctl
55#endif
56
57#if !V8_OS_NACL
58#include <sys/syscall.h>
59#endif
60
61namespace v8 {
62namespace base {
63
64namespace {
65
66// 0 is never a valid thread id.
67const pthread_t kNoThread = (pthread_t) 0;
68
69bool g_hard_abort = false;
70
71const char* g_gc_fake_mmap = NULL;
72
73}  // namespace
74
75
76int OS::ActivationFrameAlignment() {
77#if V8_TARGET_ARCH_ARM
78  // On EABI ARM targets this is required for fp correctness in the
79  // runtime system.
80  return 8;
81#elif V8_TARGET_ARCH_MIPS
82  return 8;
83#else
84  // Otherwise we just assume 16 byte alignment, i.e.:
85  // - With gcc 4.4 the tree vectorization optimizer can generate code
86  //   that requires 16 byte alignment such as movdqa on x86.
87  // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
88  //   see "Mac OS X ABI Function Call Guide"
89  return 16;
90#endif
91}
92
93
94intptr_t OS::CommitPageSize() {
95  static intptr_t page_size = getpagesize();
96  return page_size;
97}
98
99
100void OS::Free(void* address, const size_t size) {
101  // TODO(1240712): munmap has a return value which is ignored here.
102  int result = munmap(address, size);
103  USE(result);
104  DCHECK(result == 0);
105}
106
107
108// Get rid of writable permission on code allocations.
109void OS::ProtectCode(void* address, const size_t size) {
110#if V8_OS_CYGWIN
111  DWORD old_protect;
112  VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
113#elif V8_OS_NACL
114  // The Native Client port of V8 uses an interpreter, so
115  // code pages don't need PROT_EXEC.
116  mprotect(address, size, PROT_READ);
117#else
118  mprotect(address, size, PROT_READ | PROT_EXEC);
119#endif
120}
121
122
123// Create guard pages.
124void OS::Guard(void* address, const size_t size) {
125#if V8_OS_CYGWIN
126  DWORD oldprotect;
127  VirtualProtect(address, size, PAGE_NOACCESS, &oldprotect);
128#else
129  mprotect(address, size, PROT_NONE);
130#endif
131}
132
133
134static LazyInstance<RandomNumberGenerator>::type
135    platform_random_number_generator = LAZY_INSTANCE_INITIALIZER;
136
137
138void OS::Initialize(int64_t random_seed, bool hard_abort,
139                    const char* const gc_fake_mmap) {
140  if (random_seed) {
141    platform_random_number_generator.Pointer()->SetSeed(random_seed);
142  }
143  g_hard_abort = hard_abort;
144  g_gc_fake_mmap = gc_fake_mmap;
145}
146
147
148const char* OS::GetGCFakeMMapFile() {
149  return g_gc_fake_mmap;
150}
151
152
153void* OS::GetRandomMmapAddr() {
154#if V8_OS_NACL
155  // TODO(bradchen): restore randomization once Native Client gets
156  // smarter about using mmap address hints.
157  // See http://code.google.com/p/nativeclient/issues/3341
158  return NULL;
159#endif
160#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
161    defined(THREAD_SANITIZER)
162  // Dynamic tools do not support custom mmap addresses.
163  return NULL;
164#endif
165  uintptr_t raw_addr;
166  platform_random_number_generator.Pointer()->NextBytes(&raw_addr,
167                                                        sizeof(raw_addr));
168#if V8_TARGET_ARCH_X64
169  // Currently available CPUs have 48 bits of virtual addressing.  Truncate
170  // the hint address to 46 bits to give the kernel a fighting chance of
171  // fulfilling our placement request.
172  raw_addr &= V8_UINT64_C(0x3ffffffff000);
173#else
174  raw_addr &= 0x3ffff000;
175
176# ifdef __sun
177  // For our Solaris/illumos mmap hint, we pick a random address in the bottom
178  // half of the top half of the address space (that is, the third quarter).
179  // Because we do not MAP_FIXED, this will be treated only as a hint -- the
180  // system will not fail to mmap() because something else happens to already
181  // be mapped at our random address. We deliberately set the hint high enough
182  // to get well above the system's break (that is, the heap); Solaris and
183  // illumos will try the hint and if that fails allocate as if there were
184  // no hint at all. The high hint prevents the break from getting hemmed in
185  // at low values, ceding half of the address space to the system heap.
186  raw_addr += 0x80000000;
187# else
188  // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
189  // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
190  // 10.6 and 10.7.
191  raw_addr += 0x20000000;
192# endif
193#endif
194  return reinterpret_cast<void*>(raw_addr);
195}
196
197
198size_t OS::AllocateAlignment() {
199  return static_cast<size_t>(sysconf(_SC_PAGESIZE));
200}
201
202
203void OS::Sleep(int milliseconds) {
204  useconds_t ms = static_cast<useconds_t>(milliseconds);
205  usleep(1000 * ms);
206}
207
208
209void OS::Abort() {
210  if (g_hard_abort) {
211    V8_IMMEDIATE_CRASH();
212  }
213  // Redirect to std abort to signal abnormal program termination.
214  abort();
215}
216
217
218void OS::DebugBreak() {
219#if V8_HOST_ARCH_ARM
220  asm("bkpt 0");
221#elif V8_HOST_ARCH_ARM64
222  asm("brk 0");
223#elif V8_HOST_ARCH_MIPS
224  asm("break");
225#elif V8_HOST_ARCH_MIPS64
226  asm("break");
227#elif V8_HOST_ARCH_IA32
228#if V8_OS_NACL
229  asm("hlt");
230#else
231  asm("int $3");
232#endif  // V8_OS_NACL
233#elif V8_HOST_ARCH_X64
234  asm("int $3");
235#else
236#error Unsupported host architecture.
237#endif
238}
239
240
241// ----------------------------------------------------------------------------
242// Math functions
243
244double OS::nan_value() {
245  // NAN from math.h is defined in C99 and not in POSIX.
246  return NAN;
247}
248
249
250int OS::GetCurrentProcessId() {
251  return static_cast<int>(getpid());
252}
253
254
255int OS::GetCurrentThreadId() {
256#if defined(ANDROID)
257  return static_cast<int>(syscall(__NR_gettid));
258#elif defined(SYS_gettid)
259  return static_cast<int>(syscall(SYS_gettid));
260#else
261  // PNaCL doesn't have a way to get an integral thread ID, but it doesn't
262  // really matter, because we only need it in PerfJitLogger::LogRecordedBuffer.
263  return 0;
264#endif
265}
266
267
268// ----------------------------------------------------------------------------
269// POSIX date/time support.
270//
271
272int OS::GetUserTime(uint32_t* secs,  uint32_t* usecs) {
273#if V8_OS_NACL
274  // Optionally used in Logger::ResourceEvent.
275  return -1;
276#else
277  struct rusage usage;
278
279  if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
280  *secs = usage.ru_utime.tv_sec;
281  *usecs = usage.ru_utime.tv_usec;
282  return 0;
283#endif
284}
285
286
287double OS::TimeCurrentMillis() {
288  return Time::Now().ToJsTime();
289}
290
291
292class TimezoneCache {};
293
294
295TimezoneCache* OS::CreateTimezoneCache() {
296  return NULL;
297}
298
299
300void OS::DisposeTimezoneCache(TimezoneCache* cache) {
301  DCHECK(cache == NULL);
302}
303
304
305void OS::ClearTimezoneCache(TimezoneCache* cache) {
306  DCHECK(cache == NULL);
307}
308
309
310double OS::DaylightSavingsOffset(double time, TimezoneCache*) {
311  if (std::isnan(time)) return nan_value();
312  time_t tv = static_cast<time_t>(std::floor(time/msPerSecond));
313  struct tm* t = localtime(&tv);
314  if (NULL == t) return nan_value();
315  return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
316}
317
318
319int OS::GetLastError() {
320  return errno;
321}
322
323
324// ----------------------------------------------------------------------------
325// POSIX stdio support.
326//
327
328FILE* OS::FOpen(const char* path, const char* mode) {
329  FILE* file = fopen(path, mode);
330  if (file == NULL) return NULL;
331  struct stat file_stat;
332  if (fstat(fileno(file), &file_stat) != 0) return NULL;
333  bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
334  if (is_regular_file) return file;
335  fclose(file);
336  return NULL;
337}
338
339
340bool OS::Remove(const char* path) {
341  return (remove(path) == 0);
342}
343
344
345FILE* OS::OpenTemporaryFile() {
346  return tmpfile();
347}
348
349
350const char* const OS::LogFileOpenMode = "w";
351
352
353void OS::Print(const char* format, ...) {
354  va_list args;
355  va_start(args, format);
356  VPrint(format, args);
357  va_end(args);
358}
359
360
361void OS::VPrint(const char* format, va_list args) {
362#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
363  __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
364#else
365  vprintf(format, args);
366#endif
367}
368
369
370void OS::FPrint(FILE* out, const char* format, ...) {
371  va_list args;
372  va_start(args, format);
373  VFPrint(out, format, args);
374  va_end(args);
375}
376
377
378void OS::VFPrint(FILE* out, const char* format, va_list args) {
379#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
380  __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
381#else
382  vfprintf(out, format, args);
383#endif
384}
385
386
387void OS::PrintError(const char* format, ...) {
388  va_list args;
389  va_start(args, format);
390  VPrintError(format, args);
391  va_end(args);
392}
393
394
395void OS::VPrintError(const char* format, va_list args) {
396#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
397  __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
398#else
399  vfprintf(stderr, format, args);
400#endif
401}
402
403
404int OS::SNPrintF(char* str, int length, const char* format, ...) {
405  va_list args;
406  va_start(args, format);
407  int result = VSNPrintF(str, length, format, args);
408  va_end(args);
409  return result;
410}
411
412
413int OS::VSNPrintF(char* str,
414                  int length,
415                  const char* format,
416                  va_list args) {
417  int n = vsnprintf(str, length, format, args);
418  if (n < 0 || n >= length) {
419    // If the length is zero, the assignment fails.
420    if (length > 0)
421      str[length - 1] = '\0';
422    return -1;
423  } else {
424    return n;
425  }
426}
427
428
429// ----------------------------------------------------------------------------
430// POSIX string support.
431//
432
433char* OS::StrChr(char* str, int c) {
434  return strchr(str, c);
435}
436
437
438void OS::StrNCpy(char* dest, int length, const char* src, size_t n) {
439  strncpy(dest, src, n);
440}
441
442
443// ----------------------------------------------------------------------------
444// POSIX thread support.
445//
446
447class Thread::PlatformData {
448 public:
449  PlatformData() : thread_(kNoThread) {}
450  pthread_t thread_;  // Thread handle for pthread.
451  // Synchronizes thread creation
452  Mutex thread_creation_mutex_;
453};
454
455Thread::Thread(const Options& options)
456    : data_(new PlatformData),
457      stack_size_(options.stack_size()),
458      start_semaphore_(NULL) {
459  if (stack_size_ > 0 && static_cast<size_t>(stack_size_) < PTHREAD_STACK_MIN) {
460    stack_size_ = PTHREAD_STACK_MIN;
461  }
462  set_name(options.name());
463}
464
465
466Thread::~Thread() {
467  delete data_;
468}
469
470
471static void SetThreadName(const char* name) {
472#if V8_OS_DRAGONFLYBSD || V8_OS_FREEBSD || V8_OS_OPENBSD
473  pthread_set_name_np(pthread_self(), name);
474#elif V8_OS_NETBSD
475  STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
476  pthread_setname_np(pthread_self(), "%s", name);
477#elif V8_OS_MACOSX
478  // pthread_setname_np is only available in 10.6 or later, so test
479  // for it at runtime.
480  int (*dynamic_pthread_setname_np)(const char*);
481  *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
482    dlsym(RTLD_DEFAULT, "pthread_setname_np");
483  if (dynamic_pthread_setname_np == NULL)
484    return;
485
486  // Mac OS X does not expose the length limit of the name, so hardcode it.
487  static const int kMaxNameLength = 63;
488  STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
489  dynamic_pthread_setname_np(name);
490#elif defined(PR_SET_NAME)
491  prctl(PR_SET_NAME,
492        reinterpret_cast<unsigned long>(name),  // NOLINT
493        0, 0, 0);
494#endif
495}
496
497
498static void* ThreadEntry(void* arg) {
499  Thread* thread = reinterpret_cast<Thread*>(arg);
500  // We take the lock here to make sure that pthread_create finished first since
501  // we don't know which thread will run first (the original thread or the new
502  // one).
503  { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
504  SetThreadName(thread->name());
505  DCHECK(thread->data()->thread_ != kNoThread);
506  thread->NotifyStartedAndRun();
507  return NULL;
508}
509
510
511void Thread::set_name(const char* name) {
512  strncpy(name_, name, sizeof(name_));
513  name_[sizeof(name_) - 1] = '\0';
514}
515
516
517void Thread::Start() {
518  int result;
519  pthread_attr_t attr;
520  memset(&attr, 0, sizeof(attr));
521  result = pthread_attr_init(&attr);
522  DCHECK_EQ(0, result);
523  // Native client uses default stack size.
524#if !V8_OS_NACL
525  if (stack_size_ > 0) {
526    result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
527    DCHECK_EQ(0, result);
528  }
529#endif
530  {
531    LockGuard<Mutex> lock_guard(&data_->thread_creation_mutex_);
532    result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
533  }
534  DCHECK_EQ(0, result);
535  result = pthread_attr_destroy(&attr);
536  DCHECK_EQ(0, result);
537  DCHECK(data_->thread_ != kNoThread);
538  USE(result);
539}
540
541
542void Thread::Join() {
543  pthread_join(data_->thread_, NULL);
544}
545
546
547void Thread::YieldCPU() {
548  int result = sched_yield();
549  DCHECK_EQ(0, result);
550  USE(result);
551}
552
553
554static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
555#if V8_OS_CYGWIN
556  // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
557  // because pthread_key_t is a pointer type on Cygwin. This will probably not
558  // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
559  STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
560  intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
561  return static_cast<Thread::LocalStorageKey>(ptr_key);
562#else
563  return static_cast<Thread::LocalStorageKey>(pthread_key);
564#endif
565}
566
567
568static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
569#if V8_OS_CYGWIN
570  STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
571  intptr_t ptr_key = static_cast<intptr_t>(local_key);
572  return reinterpret_cast<pthread_key_t>(ptr_key);
573#else
574  return static_cast<pthread_key_t>(local_key);
575#endif
576}
577
578
579#ifdef V8_FAST_TLS_SUPPORTED
580
581static Atomic32 tls_base_offset_initialized = 0;
582intptr_t kMacTlsBaseOffset = 0;
583
584// It's safe to do the initialization more that once, but it has to be
585// done at least once.
586static void InitializeTlsBaseOffset() {
587  const size_t kBufferSize = 128;
588  char buffer[kBufferSize];
589  size_t buffer_size = kBufferSize;
590  int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
591  if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
592    V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
593  }
594  // The buffer now contains a string of the form XX.YY.ZZ, where
595  // XX is the major kernel version component.
596  // Make sure the buffer is 0-terminated.
597  buffer[kBufferSize - 1] = '\0';
598  char* period_pos = strchr(buffer, '.');
599  *period_pos = '\0';
600  int kernel_version_major =
601      static_cast<int>(strtol(buffer, NULL, 10));  // NOLINT
602  // The constants below are taken from pthreads.s from the XNU kernel
603  // sources archive at www.opensource.apple.com.
604  if (kernel_version_major < 11) {
605    // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
606    // same offsets.
607#if V8_HOST_ARCH_IA32
608    kMacTlsBaseOffset = 0x48;
609#else
610    kMacTlsBaseOffset = 0x60;
611#endif
612  } else {
613    // 11.x.x (Lion) changed the offset.
614    kMacTlsBaseOffset = 0;
615  }
616
617  Release_Store(&tls_base_offset_initialized, 1);
618}
619
620
621static void CheckFastTls(Thread::LocalStorageKey key) {
622  void* expected = reinterpret_cast<void*>(0x1234CAFE);
623  Thread::SetThreadLocal(key, expected);
624  void* actual = Thread::GetExistingThreadLocal(key);
625  if (expected != actual) {
626    V8_Fatal(__FILE__, __LINE__,
627             "V8 failed to initialize fast TLS on current kernel");
628  }
629  Thread::SetThreadLocal(key, NULL);
630}
631
632#endif  // V8_FAST_TLS_SUPPORTED
633
634
635Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
636#ifdef V8_FAST_TLS_SUPPORTED
637  bool check_fast_tls = false;
638  if (tls_base_offset_initialized == 0) {
639    check_fast_tls = true;
640    InitializeTlsBaseOffset();
641  }
642#endif
643  pthread_key_t key;
644  int result = pthread_key_create(&key, NULL);
645  DCHECK_EQ(0, result);
646  USE(result);
647  LocalStorageKey local_key = PthreadKeyToLocalKey(key);
648#ifdef V8_FAST_TLS_SUPPORTED
649  // If we just initialized fast TLS support, make sure it works.
650  if (check_fast_tls) CheckFastTls(local_key);
651#endif
652  return local_key;
653}
654
655
656void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
657  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
658  int result = pthread_key_delete(pthread_key);
659  DCHECK_EQ(0, result);
660  USE(result);
661}
662
663
664void* Thread::GetThreadLocal(LocalStorageKey key) {
665  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
666  return pthread_getspecific(pthread_key);
667}
668
669
670void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
671  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
672  int result = pthread_setspecific(pthread_key, value);
673  DCHECK_EQ(0, result);
674  USE(result);
675}
676
677
678} }  // namespace v8::base
679