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 // This module contains the platform-specific code. This make the rest of the
6 // code less dependent on operating system, compilers and runtime libraries.
7 // This module does specifically not deal with differences between different
8 // processor architecture.
9 // The platform classes have the same definition for all platforms. The
10 // implementation for a particular platform is put in platform_<os>.cc.
11 // The build system then uses the implementation for the target platform.
12 //
13 // This design has been chosen because it is simple and fast. Alternatively,
14 // the platform dependent classes could have been implemented using abstract
15 // superclasses with virtual methods and having specializations for each
16 // platform. This design was rejected because it was more complicated and
17 // slower. It would require factory methods for selecting the right
18 // implementation and the overhead of virtual methods for performance
19 // sensitive like mutex locking/unlocking.
20
21 #ifndef V8_BASE_PLATFORM_PLATFORM_H_
22 #define V8_BASE_PLATFORM_PLATFORM_H_
23
24 #include <cstdarg>
25 #include <cstdint>
26 #include <string>
27 #include <vector>
28
29 #include "src/base/base-export.h"
30 #include "src/base/build_config.h"
31 #include "src/base/compiler-specific.h"
32 #include "src/base/platform/mutex.h"
33 #include "src/base/platform/semaphore.h"
34
35 #if V8_OS_QNX
36 #include "src/base/qnx-math.h"
37 #endif
38
39 #ifdef V8_USE_ADDRESS_SANITIZER
40 #include <sanitizer/asan_interface.h>
41 #endif // V8_USE_ADDRESS_SANITIZER
42
43 namespace v8 {
44
45 namespace base {
46
47 // ----------------------------------------------------------------------------
48 // Fast TLS support
49
50 #ifndef V8_NO_FAST_TLS
51
52 #if V8_CC_MSVC && V8_HOST_ARCH_IA32
53
54 #define V8_FAST_TLS_SUPPORTED 1
55
56 V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index);
57
InternalGetExistingThreadLocal(intptr_t index)58 inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
59 const intptr_t kTibInlineTlsOffset = 0xE10;
60 const intptr_t kTibExtraTlsOffset = 0xF94;
61 const intptr_t kMaxInlineSlots = 64;
62 const intptr_t kMaxSlots = kMaxInlineSlots + 1024;
63 const intptr_t kSystemPointerSize = sizeof(void*);
64 DCHECK(0 <= index && index < kMaxSlots);
65 USE(kMaxSlots);
66 if (index < kMaxInlineSlots) {
67 return static_cast<intptr_t>(
68 __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index));
69 }
70 intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset));
71 DCHECK_NE(extra, 0);
72 return *reinterpret_cast<intptr_t*>(extra + kSystemPointerSize *
73 (index - kMaxInlineSlots));
74 }
75
76 #elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
77
78 // tvOS simulator does not use intptr_t as TLS key.
79 #if !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR)
80
81 #define V8_FAST_TLS_SUPPORTED 1
82
83 extern V8_BASE_EXPORT intptr_t kMacTlsBaseOffset;
84
85 V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index);
86
87 inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
88 intptr_t result;
89 #if V8_HOST_ARCH_IA32
90 asm("movl %%gs:(%1,%2,4), %0;"
91 :"=r"(result) // Output must be a writable register.
92 :"r"(kMacTlsBaseOffset), "r"(index));
93 #else
94 asm("movq %%gs:(%1,%2,8), %0;"
95 :"=r"(result)
96 :"r"(kMacTlsBaseOffset), "r"(index));
97 #endif
98 return result;
99 }
100
101 #endif // !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR)
102
103 #endif
104
105 #endif // V8_NO_FAST_TLS
106
107 class PageAllocator;
108 class TimezoneCache;
109
110 // ----------------------------------------------------------------------------
111 // OS
112 //
113 // This class has static methods for the different platform specific
114 // functions. Add methods here to cope with differences between the
115 // supported platforms.
116
117 class V8_BASE_EXPORT OS {
118 public:
119 // Initialize the OS class.
120 // - hard_abort: If true, OS::Abort() will crash instead of aborting.
121 // - gc_fake_mmap: Name of the file for fake gc mmap used in ll_prof.
122 static void Initialize(bool hard_abort, const char* const gc_fake_mmap);
123
124 // Returns the accumulated user time for thread. This routine
125 // can be used for profiling. The implementation should
126 // strive for high-precision timer resolution, preferable
127 // micro-second resolution.
128 static int GetUserTime(uint32_t* secs, uint32_t* usecs);
129
130 // Returns current time as the number of milliseconds since
131 // 00:00:00 UTC, January 1, 1970.
132 static double TimeCurrentMillis();
133
134 static TimezoneCache* CreateTimezoneCache();
135
136 // Returns last OS error.
137 static int GetLastError();
138
139 static FILE* FOpen(const char* path, const char* mode);
140 static bool Remove(const char* path);
141
142 static char DirectorySeparator();
143 static bool isDirectorySeparator(const char ch);
144
145 // Opens a temporary file, the file is auto removed on close.
146 static FILE* OpenTemporaryFile();
147
148 // Log file open mode is platform-dependent due to line ends issues.
149 static const char* const LogFileOpenMode;
150
151 // Print output to console. This is mostly used for debugging output.
152 // On platforms that has standard terminal output, the output
153 // should go to stdout.
154 static PRINTF_FORMAT(1, 2) void Print(const char* format, ...);
155 static PRINTF_FORMAT(1, 0) void VPrint(const char* format, va_list args);
156
157 // Print output to a file. This is mostly used for debugging output.
158 static PRINTF_FORMAT(2, 3) void FPrint(FILE* out, const char* format, ...);
159 static PRINTF_FORMAT(2, 0) void VFPrint(FILE* out, const char* format,
160 va_list args);
161
162 // Print error output to console. This is mostly used for error message
163 // output. On platforms that has standard terminal output, the output
164 // should go to stderr.
165 static PRINTF_FORMAT(1, 2) void PrintError(const char* format, ...);
166 static PRINTF_FORMAT(1, 0) void VPrintError(const char* format, va_list args);
167
168 // Memory permissions. These should be kept in sync with the ones in
169 // v8::PageAllocator.
170 enum class MemoryPermission {
171 kNoAccess,
172 kRead,
173 kReadWrite,
174 // TODO(hpayer): Remove this flag. Memory should never be rwx.
175 kReadWriteExecute,
176 kReadExecute,
177 // TODO(jkummerow): Remove this when Wasm has a platform-independent
178 // w^x implementation.
179 kNoAccessWillJitLater
180 };
181
182 static bool HasLazyCommits();
183
184 // Sleep for a specified time interval.
185 static void Sleep(TimeDelta interval);
186
187 // Abort the current process.
188 [[noreturn]] static void Abort();
189
190 // Debug break.
191 static void DebugBreak();
192
193 // Walk the stack.
194 static const int kStackWalkError = -1;
195 static const int kStackWalkMaxNameLen = 256;
196 static const int kStackWalkMaxTextLen = 256;
197 struct StackFrame {
198 void* address;
199 char text[kStackWalkMaxTextLen];
200 };
201
202 class V8_BASE_EXPORT MemoryMappedFile {
203 public:
204 enum class FileMode { kReadOnly, kReadWrite };
205
206 virtual ~MemoryMappedFile() = default;
207 virtual void* memory() const = 0;
208 virtual size_t size() const = 0;
209
210 static MemoryMappedFile* open(const char* name,
211 FileMode mode = FileMode::kReadWrite);
212 static MemoryMappedFile* create(const char* name, size_t size,
213 void* initial);
214 };
215
216 // Safe formatting print. Ensures that str is always null-terminated.
217 // Returns the number of chars written, or -1 if output was truncated.
218 static PRINTF_FORMAT(3, 4) int SNPrintF(char* str, int length,
219 const char* format, ...);
220 static PRINTF_FORMAT(3, 0) int VSNPrintF(char* str, int length,
221 const char* format, va_list args);
222
223 static void StrNCpy(char* dest, int length, const char* src, size_t n);
224
225 // Support for the profiler. Can do nothing, in which case ticks
226 // occurring in shared libraries will not be properly accounted for.
227 struct SharedLibraryAddress {
SharedLibraryAddressSharedLibraryAddress228 SharedLibraryAddress(const std::string& library_path, uintptr_t start,
229 uintptr_t end)
230 : library_path(library_path), start(start), end(end), aslr_slide(0) {}
SharedLibraryAddressSharedLibraryAddress231 SharedLibraryAddress(const std::string& library_path, uintptr_t start,
232 uintptr_t end, intptr_t aslr_slide)
233 : library_path(library_path),
234 start(start),
235 end(end),
236 aslr_slide(aslr_slide) {}
237
238 std::string library_path;
239 uintptr_t start;
240 uintptr_t end;
241 intptr_t aslr_slide;
242 };
243
244 static std::vector<SharedLibraryAddress> GetSharedLibraryAddresses();
245
246 // Support for the profiler. Notifies the external profiling
247 // process that a code moving garbage collection starts. Can do
248 // nothing, in which case the code objects must not move (e.g., by
249 // using --never-compact) if accurate profiling is desired.
250 static void SignalCodeMovingGC();
251
252 // Support runtime detection of whether the hard float option of the
253 // EABI is used.
254 static bool ArmUsingHardFloat();
255
256 // Returns the activation frame alignment constraint or zero if
257 // the platform doesn't care. Guaranteed to be a power of two.
258 static int ActivationFrameAlignment();
259
260 static int GetCurrentProcessId();
261
262 static int GetCurrentThreadId();
263
264 static void AdjustSchedulingParams();
265
266 static void ExitProcess(int exit_code);
267
268 private:
269 // These classes use the private memory management API below.
270 friend class MemoryMappedFile;
271 friend class PosixMemoryMappedFile;
272 friend class v8::base::PageAllocator;
273
274 static size_t AllocatePageSize();
275
276 static size_t CommitPageSize();
277
278 static void SetRandomMmapSeed(int64_t seed);
279
280 static void* GetRandomMmapAddr();
281
282 V8_WARN_UNUSED_RESULT static void* Allocate(void* address, size_t size,
283 size_t alignment,
284 MemoryPermission access);
285
286 V8_WARN_UNUSED_RESULT static void* AllocateShared(size_t size,
287 MemoryPermission access);
288
289 V8_WARN_UNUSED_RESULT static void* RemapShared(void* old_address,
290 void* new_address,
291 size_t size);
292
293 V8_WARN_UNUSED_RESULT static bool Free(void* address, const size_t size);
294
295 V8_WARN_UNUSED_RESULT static bool Release(void* address, size_t size);
296
297 V8_WARN_UNUSED_RESULT static bool SetPermissions(void* address, size_t size,
298 MemoryPermission access);
299
300 V8_WARN_UNUSED_RESULT static bool DiscardSystemPages(void* address,
301 size_t size);
302
303 static const int msPerSecond = 1000;
304
305 #if V8_OS_POSIX
306 static const char* GetGCFakeMMapFile();
307 #endif
308
309 DISALLOW_IMPLICIT_CONSTRUCTORS(OS);
310 };
311
312 #if (defined(_WIN32) || defined(_WIN64))
313 V8_BASE_EXPORT void EnsureConsoleOutputWin32();
314 #endif // (defined(_WIN32) || defined(_WIN64))
315
EnsureConsoleOutput()316 inline void EnsureConsoleOutput() {
317 #if (defined(_WIN32) || defined(_WIN64))
318 // Windows requires extra calls to send assert output to the console
319 // rather than a dialog box.
320 EnsureConsoleOutputWin32();
321 #endif // (defined(_WIN32) || defined(_WIN64))
322 }
323
324 // ----------------------------------------------------------------------------
325 // Thread
326 //
327 // Thread objects are used for creating and running threads. When the start()
328 // method is called the new thread starts running the run() method in the new
329 // thread. The Thread object should not be deallocated before the thread has
330 // terminated.
331
332 class V8_BASE_EXPORT Thread {
333 public:
334 // Opaque data type for thread-local storage keys.
335 #if V8_OS_STARBOARD
336 using LocalStorageKey = SbThreadLocalKey;
337 #else
338 using LocalStorageKey = int32_t;
339 #endif
340
341 class Options {
342 public:
Options()343 Options() : name_("v8:<unknown>"), stack_size_(0) {}
344 explicit Options(const char* name, int stack_size = 0)
name_(name)345 : name_(name), stack_size_(stack_size) {}
346
name()347 const char* name() const { return name_; }
stack_size()348 int stack_size() const { return stack_size_; }
349
350 private:
351 const char* name_;
352 int stack_size_;
353 };
354
355 // Create new thread.
356 explicit Thread(const Options& options);
357 Thread(const Thread&) = delete;
358 Thread& operator=(const Thread&) = delete;
359 virtual ~Thread();
360
361 // Start new thread by calling the Run() method on the new thread.
362 V8_WARN_UNUSED_RESULT bool Start();
363
364 // Start new thread and wait until Run() method is called on the new thread.
StartSynchronously()365 bool StartSynchronously() {
366 start_semaphore_ = new Semaphore(0);
367 if (!Start()) return false;
368 start_semaphore_->Wait();
369 delete start_semaphore_;
370 start_semaphore_ = nullptr;
371 return true;
372 }
373
374 // Wait until thread terminates.
375 void Join();
376
name()377 inline const char* name() const {
378 return name_;
379 }
380
381 // Abstract method for run handler.
382 virtual void Run() = 0;
383
384 // Thread-local storage.
385 static LocalStorageKey CreateThreadLocalKey();
386 static void DeleteThreadLocalKey(LocalStorageKey key);
387 static void* GetThreadLocal(LocalStorageKey key);
GetThreadLocalInt(LocalStorageKey key)388 static int GetThreadLocalInt(LocalStorageKey key) {
389 return static_cast<int>(reinterpret_cast<intptr_t>(GetThreadLocal(key)));
390 }
391 static void SetThreadLocal(LocalStorageKey key, void* value);
SetThreadLocalInt(LocalStorageKey key,int value)392 static void SetThreadLocalInt(LocalStorageKey key, int value) {
393 SetThreadLocal(key, reinterpret_cast<void*>(static_cast<intptr_t>(value)));
394 }
HasThreadLocal(LocalStorageKey key)395 static bool HasThreadLocal(LocalStorageKey key) {
396 return GetThreadLocal(key) != nullptr;
397 }
398
399 #ifdef V8_FAST_TLS_SUPPORTED
GetExistingThreadLocal(LocalStorageKey key)400 static inline void* GetExistingThreadLocal(LocalStorageKey key) {
401 void* result = reinterpret_cast<void*>(
402 InternalGetExistingThreadLocal(static_cast<intptr_t>(key)));
403 DCHECK(result == GetThreadLocal(key));
404 return result;
405 }
406 #else
GetExistingThreadLocal(LocalStorageKey key)407 static inline void* GetExistingThreadLocal(LocalStorageKey key) {
408 return GetThreadLocal(key);
409 }
410 #endif
411
412 // The thread name length is limited to 16 based on Linux's implementation of
413 // prctl().
414 static const int kMaxThreadNameLength = 16;
415
416 class PlatformData;
data()417 PlatformData* data() { return data_; }
418
NotifyStartedAndRun()419 void NotifyStartedAndRun() {
420 if (start_semaphore_) start_semaphore_->Signal();
421 Run();
422 }
423
424 private:
425 void set_name(const char* name);
426
427 PlatformData* data_;
428
429 char name_[kMaxThreadNameLength];
430 int stack_size_;
431 Semaphore* start_semaphore_;
432 };
433
434 // TODO(v8:10354): Make use of the stack utilities here in V8.
435 class V8_BASE_EXPORT Stack {
436 public:
437 // Convenience wrapper to use stack slots as unsigned values or void*
438 // pointers.
439 struct StackSlot {
440 // NOLINTNEXTLINE
StackSlotStackSlot441 StackSlot(void* value) : value(reinterpret_cast<uintptr_t>(value)) {}
StackSlotStackSlot442 StackSlot(uintptr_t value) : value(value) {} // NOLINT
443
444 // NOLINTNEXTLINE
445 operator void*() const { return reinterpret_cast<void*>(value); }
uintptr_tStackSlot446 operator uintptr_t() const { return value; } // NOLINT
447
448 uintptr_t value;
449 };
450
451 // Gets the start of the stack of the current thread.
452 static StackSlot GetStackStart();
453
454 // Returns the current stack top. Works correctly with ASAN and SafeStack.
455 // GetCurrentStackPosition() should not be inlined, because it works on stack
456 // frames if it were inlined into a function with a huge stack frame it would
457 // return an address significantly above the actual current stack position.
458 static V8_NOINLINE StackSlot GetCurrentStackPosition();
459
460 // Returns the real stack frame if slot is part of a fake frame, and slot
461 // otherwise.
GetRealStackAddressForSlot(StackSlot slot)462 static StackSlot GetRealStackAddressForSlot(StackSlot slot) {
463 #ifdef V8_USE_ADDRESS_SANITIZER
464 // ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack()
465 // call (precisely, deeper in __asan_stack_malloc_()), which results in a
466 // real frame that could be outside of stack bounds. Adjust for this
467 // impreciseness here.
468 constexpr size_t kAsanRealFrameOffsetBytes = 32;
469 void* real_frame = __asan_addr_is_in_fake_stack(
470 __asan_get_current_fake_stack(), slot, nullptr, nullptr);
471 return real_frame
472 ? (static_cast<char*>(real_frame) + kAsanRealFrameOffsetBytes)
473 : slot;
474 #endif // V8_USE_ADDRESS_SANITIZER
475 return slot;
476 }
477 };
478
479 } // namespace base
480 } // namespace v8
481
482 #endif // V8_BASE_PLATFORM_PLATFORM_H_
483