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