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 "include/v8-platform.h"
30 #include "src/base/base-export.h"
31 #include "src/base/build_config.h"
32 #include "src/base/compiler-specific.h"
33 #include "src/base/optional.h"
34 #include "src/base/platform/mutex.h"
35 #include "src/base/platform/semaphore.h"
36 #include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
37
38 #if V8_OS_QNX
39 #include "src/base/qnx-math.h"
40 #endif
41
42 #if V8_OS_FUCHSIA
43 #include <zircon/types.h>
44 #endif // V8_OS_FUCHSIA
45
46 #ifdef V8_USE_ADDRESS_SANITIZER
47 #include <sanitizer/asan_interface.h>
48 #endif // V8_USE_ADDRESS_SANITIZER
49
50 #ifndef V8_NO_FAST_TLS
51 #if V8_CC_MSVC && V8_HOST_ARCH_IA32
52 // __readfsdword is supposed to be declared in intrin.h but it is missing from
53 // some versions of that file. See https://bugs.llvm.org/show_bug.cgi?id=51188
54 // And, intrin.h is a very expensive header that we want to avoid here, and
55 // the cheaper intrin0.h is not available for all build configurations. That is
56 // why we declare this intrinsic.
57 extern "C" unsigned long __readfsdword(unsigned long); // NOLINT(runtime/int)
58 #endif // V8_CC_MSVC && V8_HOST_ARCH_IA32
59 #endif // V8_NO_FAST_TLS
60
61 namespace v8 {
62
63 namespace base {
64
65 // ----------------------------------------------------------------------------
66 // Fast TLS support
67
68 #ifndef V8_NO_FAST_TLS
69
70 #if V8_CC_MSVC && V8_HOST_ARCH_IA32
71
72 #define V8_FAST_TLS_SUPPORTED 1
73
74 V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index);
75
InternalGetExistingThreadLocal(intptr_t index)76 inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
77 const intptr_t kTibInlineTlsOffset = 0xE10;
78 const intptr_t kTibExtraTlsOffset = 0xF94;
79 const intptr_t kMaxInlineSlots = 64;
80 const intptr_t kMaxSlots = kMaxInlineSlots + 1024;
81 const intptr_t kSystemPointerSize = sizeof(void*);
82 DCHECK(0 <= index && index < kMaxSlots);
83 USE(kMaxSlots);
84 if (index < kMaxInlineSlots) {
85 return static_cast<intptr_t>(
86 __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index));
87 }
88 intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset));
89 if (!extra) return 0;
90 return *reinterpret_cast<intptr_t*>(extra + kSystemPointerSize *
91 (index - kMaxInlineSlots));
92 }
93
94 #elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
95
96 // tvOS simulator does not use intptr_t as TLS key.
97 #if !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR)
98
99 #define V8_FAST_TLS_SUPPORTED 1
100
101 extern V8_BASE_EXPORT intptr_t kMacTlsBaseOffset;
102
103 V8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index);
104
105 inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
106 intptr_t result;
107 #if V8_HOST_ARCH_IA32
108 asm("movl %%gs:(%1,%2,4), %0;"
109 :"=r"(result) // Output must be a writable register.
110 :"r"(kMacTlsBaseOffset), "r"(index));
111 #else
112 asm("movq %%gs:(%1,%2,8), %0;"
113 :"=r"(result)
114 :"r"(kMacTlsBaseOffset), "r"(index));
115 #endif
116 return result;
117 }
118
119 #endif // !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR)
120
121 #endif
122
123 #endif // V8_NO_FAST_TLS
124
125 class AddressSpaceReservation;
126 class PageAllocator;
127 class TimezoneCache;
128 class VirtualAddressSpace;
129 class VirtualAddressSubspace;
130
131 // ----------------------------------------------------------------------------
132 // OS
133 //
134 // This class has static methods for the different platform specific
135 // functions. Add methods here to cope with differences between the
136 // supported platforms.
137
138 class V8_BASE_EXPORT OS {
139 public:
140 // Initialize the OS class.
141 // - hard_abort: If true, OS::Abort() will crash instead of aborting.
142 // - gc_fake_mmap: Name of the file for fake gc mmap used in ll_prof.
143 static void Initialize(bool hard_abort, const char* const gc_fake_mmap);
144
145 #if V8_OS_WIN
146 // On Windows, ensure the newer memory API is loaded if available. This
147 // includes function like VirtualAlloc2 and MapViewOfFile3.
148 // TODO(chromium:1218005) this should probably happen as part of Initialize,
149 // but that is currently invoked too late, after the sandbox is initialized.
150 // However, eventually the sandbox initialization will probably happen as
151 // part of V8::Initialize, at which point this function can probably be
152 // merged into OS::Initialize.
153 static void EnsureWin32MemoryAPILoaded();
154 #endif
155
156 // Returns the accumulated user time for thread. This routine
157 // can be used for profiling. The implementation should
158 // strive for high-precision timer resolution, preferable
159 // micro-second resolution.
160 static int GetUserTime(uint32_t* secs, uint32_t* usecs);
161
162 // Returns current time as the number of milliseconds since
163 // 00:00:00 UTC, January 1, 1970.
164 static double TimeCurrentMillis();
165
166 static TimezoneCache* CreateTimezoneCache();
167
168 // Returns last OS error.
169 static int GetLastError();
170
171 static FILE* FOpen(const char* path, const char* mode);
172 static bool Remove(const char* path);
173
174 static char DirectorySeparator();
175 static bool isDirectorySeparator(const char ch);
176
177 // Opens a temporary file, the file is auto removed on close.
178 static FILE* OpenTemporaryFile();
179
180 // Log file open mode is platform-dependent due to line ends issues.
181 static const char* const LogFileOpenMode;
182
183 // Print output to console. This is mostly used for debugging output.
184 // On platforms that has standard terminal output, the output
185 // should go to stdout.
186 static PRINTF_FORMAT(1, 2) void Print(const char* format, ...);
187 static PRINTF_FORMAT(1, 0) void VPrint(const char* format, va_list args);
188
189 // Print output to a file. This is mostly used for debugging output.
190 static PRINTF_FORMAT(2, 3) void FPrint(FILE* out, const char* format, ...);
191 static PRINTF_FORMAT(2, 0) void VFPrint(FILE* out, const char* format,
192 va_list args);
193
194 // Print error output to console. This is mostly used for error message
195 // output. On platforms that has standard terminal output, the output
196 // should go to stderr.
197 static PRINTF_FORMAT(1, 2) void PrintError(const char* format, ...);
198 static PRINTF_FORMAT(1, 0) void VPrintError(const char* format, va_list args);
199
200 // Memory permissions. These should be kept in sync with the ones in
201 // v8::PageAllocator and v8::PagePermissions.
202 enum class MemoryPermission {
203 kNoAccess,
204 kRead,
205 kReadWrite,
206 kReadWriteExecute,
207 kReadExecute,
208 // TODO(jkummerow): Remove this when Wasm has a platform-independent
209 // w^x implementation.
210 kNoAccessWillJitLater
211 };
212
213 // Helpers to create shared memory objects. Currently only used for testing.
214 static PlatformSharedMemoryHandle CreateSharedMemoryHandleForTesting(
215 size_t size);
216 static void DestroySharedMemoryHandle(PlatformSharedMemoryHandle handle);
217
218 static bool HasLazyCommits();
219
220 // Sleep for a specified time interval.
221 static void Sleep(TimeDelta interval);
222
223 // Abort the current process.
224 [[noreturn]] static void Abort();
225
226 // Debug break.
227 static void DebugBreak();
228
229 // Walk the stack.
230 static const int kStackWalkError = -1;
231 static const int kStackWalkMaxNameLen = 256;
232 static const int kStackWalkMaxTextLen = 256;
233 struct StackFrame {
234 void* address;
235 char text[kStackWalkMaxTextLen];
236 };
237
238 class V8_BASE_EXPORT MemoryMappedFile {
239 public:
240 enum class FileMode { kReadOnly, kReadWrite };
241
242 virtual ~MemoryMappedFile() = default;
243 virtual void* memory() const = 0;
244 virtual size_t size() const = 0;
245
246 static MemoryMappedFile* open(const char* name,
247 FileMode mode = FileMode::kReadWrite);
248 static MemoryMappedFile* create(const char* name, size_t size,
249 void* initial);
250 };
251
252 // Safe formatting print. Ensures that str is always null-terminated.
253 // Returns the number of chars written, or -1 if output was truncated.
254 static PRINTF_FORMAT(3, 4) int SNPrintF(char* str, int length,
255 const char* format, ...);
256 static PRINTF_FORMAT(3, 0) int VSNPrintF(char* str, int length,
257 const char* format, va_list args);
258
259 static void StrNCpy(char* dest, int length, const char* src, size_t n);
260
261 // Support for the profiler. Can do nothing, in which case ticks
262 // occurring in shared libraries will not be properly accounted for.
263 struct SharedLibraryAddress {
SharedLibraryAddressSharedLibraryAddress264 SharedLibraryAddress(const std::string& library_path, uintptr_t start,
265 uintptr_t end)
266 : library_path(library_path), start(start), end(end), aslr_slide(0) {}
SharedLibraryAddressSharedLibraryAddress267 SharedLibraryAddress(const std::string& library_path, uintptr_t start,
268 uintptr_t end, intptr_t aslr_slide)
269 : library_path(library_path),
270 start(start),
271 end(end),
272 aslr_slide(aslr_slide) {}
273
274 std::string library_path;
275 uintptr_t start;
276 uintptr_t end;
277 intptr_t aslr_slide;
278 };
279
280 static std::vector<SharedLibraryAddress> GetSharedLibraryAddresses();
281
282 // Support for the profiler. Notifies the external profiling
283 // process that a code moving garbage collection starts. Can do
284 // nothing, in which case the code objects must not move (e.g., by
285 // using --never-compact) if accurate profiling is desired.
286 static void SignalCodeMovingGC();
287
288 // Support runtime detection of whether the hard float option of the
289 // EABI is used.
290 static bool ArmUsingHardFloat();
291
292 // Returns the activation frame alignment constraint or zero if
293 // the platform doesn't care. Guaranteed to be a power of two.
294 static int ActivationFrameAlignment();
295
296 static int GetCurrentProcessId();
297
298 static int GetCurrentThreadId();
299
300 static void AdjustSchedulingParams();
301
302 using Address = uintptr_t;
303
304 struct MemoryRange {
305 uintptr_t start = 0;
306 uintptr_t end = 0;
307 };
308
309 // Find gaps between existing virtual memory ranges that have enough space
310 // to place a region with minimum_size within (boundary_start, boundary_end)
311 static std::vector<MemoryRange> GetFreeMemoryRangesWithin(
312 Address boundary_start, Address boundary_end, size_t minimum_size,
313 size_t alignment);
314
315 [[noreturn]] static void ExitProcess(int exit_code);
316
317 // Whether the platform supports mapping a given address in another location
318 // in the address space.
IsRemapPageSupported()319 V8_WARN_UNUSED_RESULT static constexpr bool IsRemapPageSupported() {
320 #ifdef V8_OS_MACOS
321 return true;
322 #else
323 return false;
324 #endif
325 }
326
327 // Remaps already-mapped memory at |new_address| with |access| permissions.
328 //
329 // Both the source and target addresses must be page-aligned, and |size| must
330 // be a multiple of the system page size. If there is already memory mapped
331 // at the target address, it is replaced by the new mapping.
332 //
333 // Must not be called if |IsRemapPagesSupported()| return false.
334 // Returns true for success.
335 V8_WARN_UNUSED_RESULT static bool RemapPages(const void* address, size_t size,
336 void* new_address,
337 MemoryPermission access);
338
339 private:
340 // These classes use the private memory management API below.
341 friend class AddressSpaceReservation;
342 friend class MemoryMappedFile;
343 friend class PosixMemoryMappedFile;
344 friend class v8::base::PageAllocator;
345 friend class v8::base::VirtualAddressSpace;
346 friend class v8::base::VirtualAddressSubspace;
347 FRIEND_TEST(OS, RemapPages);
348
349 static size_t AllocatePageSize();
350
351 static size_t CommitPageSize();
352
353 static void SetRandomMmapSeed(int64_t seed);
354
355 static void* GetRandomMmapAddr();
356
357 V8_WARN_UNUSED_RESULT static void* Allocate(void* address, size_t size,
358 size_t alignment,
359 MemoryPermission access);
360
361 V8_WARN_UNUSED_RESULT static void* AllocateShared(size_t size,
362 MemoryPermission access);
363
364 V8_WARN_UNUSED_RESULT static void* RemapShared(void* old_address,
365 void* new_address,
366 size_t size);
367
368 static void Free(void* address, size_t size);
369
370 V8_WARN_UNUSED_RESULT static void* AllocateShared(
371 void* address, size_t size, OS::MemoryPermission access,
372 PlatformSharedMemoryHandle handle, uint64_t offset);
373
374 static void FreeShared(void* address, size_t size);
375
376 static void Release(void* address, size_t size);
377
378 V8_WARN_UNUSED_RESULT static bool SetPermissions(void* address, size_t size,
379 MemoryPermission access);
380
381 V8_WARN_UNUSED_RESULT static bool DiscardSystemPages(void* address,
382 size_t size);
383
384 V8_WARN_UNUSED_RESULT static bool DecommitPages(void* address, size_t size);
385
386 V8_WARN_UNUSED_RESULT static bool CanReserveAddressSpace();
387
388 V8_WARN_UNUSED_RESULT static Optional<AddressSpaceReservation>
389 CreateAddressSpaceReservation(void* hint, size_t size, size_t alignment,
390 MemoryPermission max_permission);
391
392 static void FreeAddressSpaceReservation(AddressSpaceReservation reservation);
393
394 static const int msPerSecond = 1000;
395
396 #if V8_OS_POSIX
397 static const char* GetGCFakeMMapFile();
398 #endif
399
400 DISALLOW_IMPLICIT_CONSTRUCTORS(OS);
401 };
402
403 #if (defined(_WIN32) || defined(_WIN64))
404 V8_BASE_EXPORT void EnsureConsoleOutputWin32();
405 #endif // (defined(_WIN32) || defined(_WIN64))
406
EnsureConsoleOutput()407 inline void EnsureConsoleOutput() {
408 #if (defined(_WIN32) || defined(_WIN64))
409 // Windows requires extra calls to send assert output to the console
410 // rather than a dialog box.
411 EnsureConsoleOutputWin32();
412 #endif // (defined(_WIN32) || defined(_WIN64))
413 }
414
415 // ----------------------------------------------------------------------------
416 // AddressSpaceReservation
417 //
418 // This class provides the same memory management functions as OS but operates
419 // inside a previously reserved contiguous region of virtual address space.
420 //
421 // Reserved address space in which no pages have been allocated is guaranteed
422 // to be inaccessible and cause a fault on access. As such, creating guard
423 // regions requires no further action.
424 class V8_BASE_EXPORT AddressSpaceReservation {
425 public:
426 using Address = uintptr_t;
427
base()428 void* base() const { return base_; }
size()429 size_t size() const { return size_; }
430
Contains(void * region_addr,size_t region_size)431 bool Contains(void* region_addr, size_t region_size) const {
432 Address base = reinterpret_cast<Address>(base_);
433 Address region_base = reinterpret_cast<Address>(region_addr);
434 return (region_base >= base) &&
435 ((region_base + region_size) <= (base + size_));
436 }
437
438 V8_WARN_UNUSED_RESULT bool Allocate(void* address, size_t size,
439 OS::MemoryPermission access);
440
441 V8_WARN_UNUSED_RESULT bool Free(void* address, size_t size);
442
443 V8_WARN_UNUSED_RESULT bool AllocateShared(void* address, size_t size,
444 OS::MemoryPermission access,
445 PlatformSharedMemoryHandle handle,
446 uint64_t offset);
447
448 V8_WARN_UNUSED_RESULT bool FreeShared(void* address, size_t size);
449
450 V8_WARN_UNUSED_RESULT bool SetPermissions(void* address, size_t size,
451 OS::MemoryPermission access);
452
453 V8_WARN_UNUSED_RESULT bool DiscardSystemPages(void* address, size_t size);
454
455 V8_WARN_UNUSED_RESULT bool DecommitPages(void* address, size_t size);
456
457 V8_WARN_UNUSED_RESULT Optional<AddressSpaceReservation> CreateSubReservation(
458 void* address, size_t size, OS::MemoryPermission max_permission);
459
460 V8_WARN_UNUSED_RESULT static bool FreeSubReservation(
461 AddressSpaceReservation reservation);
462
463 #if V8_OS_WIN
464 // On Windows, the placeholder mappings backing address space reservations
465 // need to be split and merged as page allocations can only replace an entire
466 // placeholder mapping, not parts of it. This must be done by the users of
467 // this API as it requires a RegionAllocator (or equivalent) to keep track of
468 // sub-regions and decide when to split and when to coalesce multiple free
469 // regions into a single one.
470 V8_WARN_UNUSED_RESULT bool SplitPlaceholder(void* address, size_t size);
471 V8_WARN_UNUSED_RESULT bool MergePlaceholders(void* address, size_t size);
472 #endif // V8_OS_WIN
473
474 private:
475 friend class OS;
476
477 #if V8_OS_FUCHSIA
AddressSpaceReservation(void * base,size_t size,zx_handle_t vmar)478 AddressSpaceReservation(void* base, size_t size, zx_handle_t vmar)
479 : base_(base), size_(size), vmar_(vmar) {}
480 #else
AddressSpaceReservation(void * base,size_t size)481 AddressSpaceReservation(void* base, size_t size) : base_(base), size_(size) {}
482 #endif // V8_OS_FUCHSIA
483
484 void* base_ = nullptr;
485 size_t size_ = 0;
486
487 #if V8_OS_FUCHSIA
488 // On Fuchsia, address space reservations are backed by VMARs.
489 zx_handle_t vmar_ = ZX_HANDLE_INVALID;
490 #endif // V8_OS_FUCHSIA
491 };
492
493 // ----------------------------------------------------------------------------
494 // Thread
495 //
496 // Thread objects are used for creating and running threads. When the start()
497 // method is called the new thread starts running the run() method in the new
498 // thread. The Thread object should not be deallocated before the thread has
499 // terminated.
500
501 class V8_BASE_EXPORT Thread {
502 public:
503 // Opaque data type for thread-local storage keys.
504 #if V8_OS_STARBOARD
505 using LocalStorageKey = SbThreadLocalKey;
506 #else
507 using LocalStorageKey = int32_t;
508 #endif
509
510 class Options {
511 public:
Options()512 Options() : name_("v8:<unknown>"), stack_size_(0) {}
513 explicit Options(const char* name, int stack_size = 0)
name_(name)514 : name_(name), stack_size_(stack_size) {}
515
name()516 const char* name() const { return name_; }
stack_size()517 int stack_size() const { return stack_size_; }
518
519 private:
520 const char* name_;
521 int stack_size_;
522 };
523
524 // Create new thread.
525 explicit Thread(const Options& options);
526 Thread(const Thread&) = delete;
527 Thread& operator=(const Thread&) = delete;
528 virtual ~Thread();
529
530 // Start new thread by calling the Run() method on the new thread.
531 V8_WARN_UNUSED_RESULT bool Start();
532
533 // Start new thread and wait until Run() method is called on the new thread.
StartSynchronously()534 bool StartSynchronously() {
535 start_semaphore_ = new Semaphore(0);
536 if (!Start()) return false;
537 start_semaphore_->Wait();
538 delete start_semaphore_;
539 start_semaphore_ = nullptr;
540 return true;
541 }
542
543 // Wait until thread terminates.
544 void Join();
545
name()546 inline const char* name() const {
547 return name_;
548 }
549
550 // Abstract method for run handler.
551 virtual void Run() = 0;
552
553 // Thread-local storage.
554 static LocalStorageKey CreateThreadLocalKey();
555 static void DeleteThreadLocalKey(LocalStorageKey key);
556 static void* GetThreadLocal(LocalStorageKey key);
557 static void SetThreadLocal(LocalStorageKey key, void* value);
HasThreadLocal(LocalStorageKey key)558 static bool HasThreadLocal(LocalStorageKey key) {
559 return GetThreadLocal(key) != nullptr;
560 }
561
562 #ifdef V8_FAST_TLS_SUPPORTED
GetExistingThreadLocal(LocalStorageKey key)563 static inline void* GetExistingThreadLocal(LocalStorageKey key) {
564 void* result = reinterpret_cast<void*>(
565 InternalGetExistingThreadLocal(static_cast<intptr_t>(key)));
566 DCHECK(result == GetThreadLocal(key));
567 return result;
568 }
569 #else
GetExistingThreadLocal(LocalStorageKey key)570 static inline void* GetExistingThreadLocal(LocalStorageKey key) {
571 return GetThreadLocal(key);
572 }
573 #endif
574
575 // The thread name length is limited to 16 based on Linux's implementation of
576 // prctl().
577 static const int kMaxThreadNameLength = 16;
578
579 class PlatformData;
data()580 PlatformData* data() { return data_; }
581
NotifyStartedAndRun()582 void NotifyStartedAndRun() {
583 if (start_semaphore_) start_semaphore_->Signal();
584 Run();
585 }
586
587 private:
588 void set_name(const char* name);
589
590 PlatformData* data_;
591
592 char name_[kMaxThreadNameLength];
593 int stack_size_;
594 Semaphore* start_semaphore_;
595 };
596
597 // TODO(v8:10354): Make use of the stack utilities here in V8.
598 class V8_BASE_EXPORT Stack {
599 public:
600 // Convenience wrapper to use stack slots as unsigned values or void*
601 // pointers.
602 struct StackSlot {
603 // NOLINTNEXTLINE
StackSlotStackSlot604 StackSlot(void* value) : value(reinterpret_cast<uintptr_t>(value)) {}
StackSlotStackSlot605 StackSlot(uintptr_t value) : value(value) {} // NOLINT
606
607 // NOLINTNEXTLINE
608 operator void*() const { return reinterpret_cast<void*>(value); }
uintptr_tStackSlot609 operator uintptr_t() const { return value; } // NOLINT
610
611 uintptr_t value;
612 };
613
614 // Gets the start of the stack of the current thread.
615 static StackSlot GetStackStart();
616
617 // Returns the current stack top. Works correctly with ASAN and SafeStack.
618 // GetCurrentStackPosition() should not be inlined, because it works on stack
619 // frames if it were inlined into a function with a huge stack frame it would
620 // return an address significantly above the actual current stack position.
621 static V8_NOINLINE StackSlot GetCurrentStackPosition();
622
623 // Returns the real stack frame if slot is part of a fake frame, and slot
624 // otherwise.
GetRealStackAddressForSlot(StackSlot slot)625 static StackSlot GetRealStackAddressForSlot(StackSlot slot) {
626 #ifdef V8_USE_ADDRESS_SANITIZER
627 // ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack()
628 // call (precisely, deeper in __asan_stack_malloc_()), which results in a
629 // real frame that could be outside of stack bounds. Adjust for this
630 // impreciseness here.
631 constexpr size_t kAsanRealFrameOffsetBytes = 32;
632 void* real_frame = __asan_addr_is_in_fake_stack(
633 __asan_get_current_fake_stack(), slot, nullptr, nullptr);
634 return real_frame
635 ? (static_cast<char*>(real_frame) + kAsanRealFrameOffsetBytes)
636 : slot;
637 #endif // V8_USE_ADDRESS_SANITIZER
638 return slot;
639 }
640 };
641
642 } // namespace base
643 } // namespace v8
644
645 #endif // V8_BASE_PLATFORM_PLATFORM_H_
646