| // Copyright 2012 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This module contains the platform-specific code. This make the rest of the |
| // code less dependent on operating system, compilers and runtime libraries. |
| // This module does specifically not deal with differences between different |
| // processor architecture. |
| // The platform classes have the same definition for all platforms. The |
| // implementation for a particular platform is put in platform_<os>.cc. |
| // The build system then uses the implementation for the target platform. |
| // |
| // This design has been chosen because it is simple and fast. Alternatively, |
| // the platform dependent classes could have been implemented using abstract |
| // superclasses with virtual methods and having specializations for each |
| // platform. This design was rejected because it was more complicated and |
| // slower. It would require factory methods for selecting the right |
| // implementation and the overhead of virtual methods for performance |
| // sensitive like mutex locking/unlocking. |
| |
| #ifndef V8_BASE_PLATFORM_PLATFORM_H_ |
| #define V8_BASE_PLATFORM_PLATFORM_H_ |
| |
| #include <cstdarg> |
| #include <cstdint> |
| #include <string> |
| #include <vector> |
| |
| #include "src/base/base-export.h" |
| #include "src/base/build_config.h" |
| #include "src/base/compiler-specific.h" |
| #include "src/base/platform/mutex.h" |
| #include "src/base/platform/semaphore.h" |
| |
| #if V8_OS_QNX |
| #include "src/base/qnx-math.h" |
| #endif |
| |
| #ifdef V8_USE_ADDRESS_SANITIZER |
| #include <sanitizer/asan_interface.h> |
| #endif // V8_USE_ADDRESS_SANITIZER |
| |
| namespace v8 { |
| |
| namespace base { |
| |
| // ---------------------------------------------------------------------------- |
| // Fast TLS support |
| |
| #ifndef V8_NO_FAST_TLS |
| |
| #if V8_CC_MSVC && V8_HOST_ARCH_IA32 |
| |
| #define V8_FAST_TLS_SUPPORTED 1 |
| |
| V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index); |
| |
| inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { |
| const intptr_t kTibInlineTlsOffset = 0xE10; |
| const intptr_t kTibExtraTlsOffset = 0xF94; |
| const intptr_t kMaxInlineSlots = 64; |
| const intptr_t kMaxSlots = kMaxInlineSlots + 1024; |
| const intptr_t kSystemPointerSize = sizeof(void*); |
| DCHECK(0 <= index && index < kMaxSlots); |
| USE(kMaxSlots); |
| if (index < kMaxInlineSlots) { |
| return static_cast<intptr_t>( |
| __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index)); |
| } |
| intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset)); |
| DCHECK_NE(extra, 0); |
| return *reinterpret_cast<intptr_t*>(extra + kSystemPointerSize * |
| (index - kMaxInlineSlots)); |
| } |
| |
| #elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64) |
| |
| // tvOS simulator does not use intptr_t as TLS key. |
| #if !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR) |
| |
| #define V8_FAST_TLS_SUPPORTED 1 |
| |
| extern V8_BASE_EXPORT intptr_t kMacTlsBaseOffset; |
| |
| V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index); |
| |
| inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { |
| intptr_t result; |
| #if V8_HOST_ARCH_IA32 |
| asm("movl %%gs:(%1,%2,4), %0;" |
| :"=r"(result) // Output must be a writable register. |
| :"r"(kMacTlsBaseOffset), "r"(index)); |
| #else |
| asm("movq %%gs:(%1,%2,8), %0;" |
| :"=r"(result) |
| :"r"(kMacTlsBaseOffset), "r"(index)); |
| #endif |
| return result; |
| } |
| |
| #endif // !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR) |
| |
| #endif |
| |
| #endif // V8_NO_FAST_TLS |
| |
| class PageAllocator; |
| class TimezoneCache; |
| |
| // ---------------------------------------------------------------------------- |
| // OS |
| // |
| // This class has static methods for the different platform specific |
| // functions. Add methods here to cope with differences between the |
| // supported platforms. |
| |
| class V8_BASE_EXPORT OS { |
| public: |
| // Initialize the OS class. |
| // - hard_abort: If true, OS::Abort() will crash instead of aborting. |
| // - gc_fake_mmap: Name of the file for fake gc mmap used in ll_prof. |
| static void Initialize(bool hard_abort, const char* const gc_fake_mmap); |
| |
| // Returns the accumulated user time for thread. This routine |
| // can be used for profiling. The implementation should |
| // strive for high-precision timer resolution, preferable |
| // micro-second resolution. |
| static int GetUserTime(uint32_t* secs, uint32_t* usecs); |
| |
| // Returns current time as the number of milliseconds since |
| // 00:00:00 UTC, January 1, 1970. |
| static double TimeCurrentMillis(); |
| |
| static TimezoneCache* CreateTimezoneCache(); |
| |
| // Returns last OS error. |
| static int GetLastError(); |
| |
| static FILE* FOpen(const char* path, const char* mode); |
| static bool Remove(const char* path); |
| |
| static char DirectorySeparator(); |
| static bool isDirectorySeparator(const char ch); |
| |
| // Opens a temporary file, the file is auto removed on close. |
| static FILE* OpenTemporaryFile(); |
| |
| // Log file open mode is platform-dependent due to line ends issues. |
| static const char* const LogFileOpenMode; |
| |
| // Print output to console. This is mostly used for debugging output. |
| // On platforms that has standard terminal output, the output |
| // should go to stdout. |
| static PRINTF_FORMAT(1, 2) void Print(const char* format, ...); |
| static PRINTF_FORMAT(1, 0) void VPrint(const char* format, va_list args); |
| |
| // Print output to a file. This is mostly used for debugging output. |
| static PRINTF_FORMAT(2, 3) void FPrint(FILE* out, const char* format, ...); |
| static PRINTF_FORMAT(2, 0) void VFPrint(FILE* out, const char* format, |
| va_list args); |
| |
| // Print error output to console. This is mostly used for error message |
| // output. On platforms that has standard terminal output, the output |
| // should go to stderr. |
| static PRINTF_FORMAT(1, 2) void PrintError(const char* format, ...); |
| static PRINTF_FORMAT(1, 0) void VPrintError(const char* format, va_list args); |
| |
| // Memory permissions. These should be kept in sync with the ones in |
| // v8::PageAllocator. |
| enum class MemoryPermission { |
| kNoAccess, |
| kRead, |
| kReadWrite, |
| // TODO(hpayer): Remove this flag. Memory should never be rwx. |
| kReadWriteExecute, |
| kReadExecute, |
| // TODO(jkummerow): Remove this when Wasm has a platform-independent |
| // w^x implementation. |
| kNoAccessWillJitLater |
| }; |
| |
| static bool HasLazyCommits(); |
| |
| // Sleep for a specified time interval. |
| static void Sleep(TimeDelta interval); |
| |
| // Abort the current process. |
| [[noreturn]] static void Abort(); |
| |
| // Debug break. |
| static void DebugBreak(); |
| |
| // Walk the stack. |
| static const int kStackWalkError = -1; |
| static const int kStackWalkMaxNameLen = 256; |
| static const int kStackWalkMaxTextLen = 256; |
| struct StackFrame { |
| void* address; |
| char text[kStackWalkMaxTextLen]; |
| }; |
| |
| class V8_BASE_EXPORT MemoryMappedFile { |
| public: |
| enum class FileMode { kReadOnly, kReadWrite }; |
| |
| virtual ~MemoryMappedFile() = default; |
| virtual void* memory() const = 0; |
| virtual size_t size() const = 0; |
| |
| static MemoryMappedFile* open(const char* name, |
| FileMode mode = FileMode::kReadWrite); |
| static MemoryMappedFile* create(const char* name, size_t size, |
| void* initial); |
| }; |
| |
| // Safe formatting print. Ensures that str is always null-terminated. |
| // Returns the number of chars written, or -1 if output was truncated. |
| static PRINTF_FORMAT(3, 4) int SNPrintF(char* str, int length, |
| const char* format, ...); |
| static PRINTF_FORMAT(3, 0) int VSNPrintF(char* str, int length, |
| const char* format, va_list args); |
| |
| static void StrNCpy(char* dest, int length, const char* src, size_t n); |
| |
| // Support for the profiler. Can do nothing, in which case ticks |
| // occurring in shared libraries will not be properly accounted for. |
| struct SharedLibraryAddress { |
| SharedLibraryAddress(const std::string& library_path, uintptr_t start, |
| uintptr_t end) |
| : library_path(library_path), start(start), end(end), aslr_slide(0) {} |
| SharedLibraryAddress(const std::string& library_path, uintptr_t start, |
| uintptr_t end, intptr_t aslr_slide) |
| : library_path(library_path), |
| start(start), |
| end(end), |
| aslr_slide(aslr_slide) {} |
| |
| std::string library_path; |
| uintptr_t start; |
| uintptr_t end; |
| intptr_t aslr_slide; |
| }; |
| |
| static std::vector<SharedLibraryAddress> GetSharedLibraryAddresses(); |
| |
| // Support for the profiler. Notifies the external profiling |
| // process that a code moving garbage collection starts. Can do |
| // nothing, in which case the code objects must not move (e.g., by |
| // using --never-compact) if accurate profiling is desired. |
| static void SignalCodeMovingGC(); |
| |
| // Support runtime detection of whether the hard float option of the |
| // EABI is used. |
| static bool ArmUsingHardFloat(); |
| |
| // Returns the activation frame alignment constraint or zero if |
| // the platform doesn't care. Guaranteed to be a power of two. |
| static int ActivationFrameAlignment(); |
| |
| static int GetCurrentProcessId(); |
| |
| static int GetCurrentThreadId(); |
| |
| static void AdjustSchedulingParams(); |
| |
| static void ExitProcess(int exit_code); |
| |
| private: |
| // These classes use the private memory management API below. |
| friend class MemoryMappedFile; |
| friend class PosixMemoryMappedFile; |
| friend class v8::base::PageAllocator; |
| |
| static size_t AllocatePageSize(); |
| |
| static size_t CommitPageSize(); |
| |
| static void SetRandomMmapSeed(int64_t seed); |
| |
| static void* GetRandomMmapAddr(); |
| |
| V8_WARN_UNUSED_RESULT static void* Allocate(void* address, size_t size, |
| size_t alignment, |
| MemoryPermission access); |
| |
| V8_WARN_UNUSED_RESULT static void* AllocateShared(size_t size, |
| MemoryPermission access); |
| |
| V8_WARN_UNUSED_RESULT static void* RemapShared(void* old_address, |
| void* new_address, |
| size_t size); |
| |
| V8_WARN_UNUSED_RESULT static bool Free(void* address, const size_t size); |
| |
| V8_WARN_UNUSED_RESULT static bool Release(void* address, size_t size); |
| |
| V8_WARN_UNUSED_RESULT static bool SetPermissions(void* address, size_t size, |
| MemoryPermission access); |
| |
| V8_WARN_UNUSED_RESULT static bool DiscardSystemPages(void* address, |
| size_t size); |
| |
| static const int msPerSecond = 1000; |
| |
| #if V8_OS_POSIX |
| static const char* GetGCFakeMMapFile(); |
| #endif |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(OS); |
| }; |
| |
| #if (defined(_WIN32) || defined(_WIN64)) |
| V8_BASE_EXPORT void EnsureConsoleOutputWin32(); |
| #endif // (defined(_WIN32) || defined(_WIN64)) |
| |
| inline void EnsureConsoleOutput() { |
| #if (defined(_WIN32) || defined(_WIN64)) |
| // Windows requires extra calls to send assert output to the console |
| // rather than a dialog box. |
| EnsureConsoleOutputWin32(); |
| #endif // (defined(_WIN32) || defined(_WIN64)) |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Thread |
| // |
| // Thread objects are used for creating and running threads. When the start() |
| // method is called the new thread starts running the run() method in the new |
| // thread. The Thread object should not be deallocated before the thread has |
| // terminated. |
| |
| class V8_BASE_EXPORT Thread { |
| public: |
| // Opaque data type for thread-local storage keys. |
| #if V8_OS_STARBOARD |
| using LocalStorageKey = uintptr_t; |
| #else |
| using LocalStorageKey = int32_t; |
| #endif |
| |
| class Options { |
| public: |
| Options() : name_("v8:<unknown>"), stack_size_(0) {} |
| explicit Options(const char* name, int stack_size = 0) |
| : name_(name), stack_size_(stack_size) {} |
| |
| const char* name() const { return name_; } |
| int stack_size() const { return stack_size_; } |
| |
| private: |
| const char* name_; |
| int stack_size_; |
| }; |
| |
| // Create new thread. |
| explicit Thread(const Options& options); |
| Thread(const Thread&) = delete; |
| Thread& operator=(const Thread&) = delete; |
| virtual ~Thread(); |
| |
| // Start new thread by calling the Run() method on the new thread. |
| V8_WARN_UNUSED_RESULT bool Start(); |
| |
| // Start new thread and wait until Run() method is called on the new thread. |
| bool StartSynchronously() { |
| start_semaphore_ = new Semaphore(0); |
| if (!Start()) return false; |
| start_semaphore_->Wait(); |
| delete start_semaphore_; |
| start_semaphore_ = nullptr; |
| return true; |
| } |
| |
| // Wait until thread terminates. |
| void Join(); |
| |
| inline const char* name() const { |
| return name_; |
| } |
| |
| // Abstract method for run handler. |
| virtual void Run() = 0; |
| |
| // Thread-local storage. |
| static LocalStorageKey CreateThreadLocalKey(); |
| static void DeleteThreadLocalKey(LocalStorageKey key); |
| static void* GetThreadLocal(LocalStorageKey key); |
| static int GetThreadLocalInt(LocalStorageKey key) { |
| return static_cast<int>(reinterpret_cast<intptr_t>(GetThreadLocal(key))); |
| } |
| static void SetThreadLocal(LocalStorageKey key, void* value); |
| static void SetThreadLocalInt(LocalStorageKey key, int value) { |
| SetThreadLocal(key, reinterpret_cast<void*>(static_cast<intptr_t>(value))); |
| } |
| static bool HasThreadLocal(LocalStorageKey key) { |
| return GetThreadLocal(key) != nullptr; |
| } |
| |
| #ifdef V8_FAST_TLS_SUPPORTED |
| static inline void* GetExistingThreadLocal(LocalStorageKey key) { |
| void* result = reinterpret_cast<void*>( |
| InternalGetExistingThreadLocal(static_cast<intptr_t>(key))); |
| DCHECK(result == GetThreadLocal(key)); |
| return result; |
| } |
| #else |
| static inline void* GetExistingThreadLocal(LocalStorageKey key) { |
| return GetThreadLocal(key); |
| } |
| #endif |
| |
| // The thread name length is limited to 16 based on Linux's implementation of |
| // prctl(). |
| static const int kMaxThreadNameLength = 16; |
| |
| class PlatformData; |
| PlatformData* data() { return data_; } |
| |
| void NotifyStartedAndRun() { |
| if (start_semaphore_) start_semaphore_->Signal(); |
| Run(); |
| } |
| |
| private: |
| void set_name(const char* name); |
| |
| PlatformData* data_; |
| |
| char name_[kMaxThreadNameLength]; |
| int stack_size_; |
| Semaphore* start_semaphore_; |
| }; |
| |
| // TODO(v8:10354): Make use of the stack utilities here in V8. |
| class V8_BASE_EXPORT Stack { |
| public: |
| // Convenience wrapper to use stack slots as unsigned values or void* |
| // pointers. |
| struct StackSlot { |
| // NOLINTNEXTLINE |
| StackSlot(void* value) : value(reinterpret_cast<uintptr_t>(value)) {} |
| StackSlot(uintptr_t value) : value(value) {} // NOLINT |
| |
| // NOLINTNEXTLINE |
| operator void*() const { return reinterpret_cast<void*>(value); } |
| operator uintptr_t() const { return value; } // NOLINT |
| |
| uintptr_t value; |
| }; |
| |
| // Gets the start of the stack of the current thread. |
| static StackSlot GetStackStart(); |
| |
| // Returns the current stack top. Works correctly with ASAN and SafeStack. |
| // GetCurrentStackPosition() should not be inlined, because it works on stack |
| // frames if it were inlined into a function with a huge stack frame it would |
| // return an address significantly above the actual current stack position. |
| static V8_NOINLINE StackSlot GetCurrentStackPosition(); |
| |
| // Returns the real stack frame if slot is part of a fake frame, and slot |
| // otherwise. |
| static StackSlot GetRealStackAddressForSlot(StackSlot slot) { |
| #ifdef V8_USE_ADDRESS_SANITIZER |
| // ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack() |
| // call (precisely, deeper in __asan_stack_malloc_()), which results in a |
| // real frame that could be outside of stack bounds. Adjust for this |
| // impreciseness here. |
| constexpr size_t kAsanRealFrameOffsetBytes = 32; |
| void* real_frame = __asan_addr_is_in_fake_stack( |
| __asan_get_current_fake_stack(), slot, nullptr, nullptr); |
| return real_frame |
| ? (static_cast<char*>(real_frame) + kAsanRealFrameOffsetBytes) |
| : slot; |
| #endif // V8_USE_ADDRESS_SANITIZER |
| return slot; |
| } |
| }; |
| |
| } // namespace base |
| } // namespace v8 |
| |
| #endif // V8_BASE_PLATFORM_PLATFORM_H_ |