1// Copyright 2018 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#ifndef INCLUDE_V8_INTERNAL_H_ 6#define INCLUDE_V8_INTERNAL_H_ 7 8#include <stddef.h> 9#include <stdint.h> 10#include <string.h> 11 12#include <atomic> 13#include <type_traits> 14 15#include "v8-version.h" // NOLINT(build/include_directory) 16#include "v8config.h" // NOLINT(build/include_directory) 17 18namespace v8 { 19 20class Array; 21class Context; 22class Data; 23class Isolate; 24 25namespace internal { 26 27class Isolate; 28 29typedef uintptr_t Address; 30static constexpr Address kNullAddress = 0; 31 32constexpr int KB = 1024; 33constexpr int MB = KB * 1024; 34constexpr int GB = MB * 1024; 35#ifdef V8_TARGET_ARCH_X64 36constexpr size_t TB = size_t{GB} * 1024; 37#endif 38 39/** 40 * Configuration of tagging scheme. 41 */ 42const int kApiSystemPointerSize = sizeof(void*); 43const int kApiDoubleSize = sizeof(double); 44const int kApiInt32Size = sizeof(int32_t); 45const int kApiInt64Size = sizeof(int64_t); 46const int kApiSizetSize = sizeof(size_t); 47 48// Tag information for HeapObject. 49const int kHeapObjectTag = 1; 50const int kWeakHeapObjectTag = 3; 51const int kHeapObjectTagSize = 2; 52const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; 53const intptr_t kHeapObjectReferenceTagMask = 1 << (kHeapObjectTagSize - 1); 54 55// Tag information for fowarding pointers stored in object headers. 56// 0b00 at the lowest 2 bits in the header indicates that the map word is a 57// forwarding pointer. 58const int kForwardingTag = 0; 59const int kForwardingTagSize = 2; 60const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1; 61 62// Tag information for Smi. 63const int kSmiTag = 0; 64const int kSmiTagSize = 1; 65const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1; 66 67template <size_t tagged_ptr_size> 68struct SmiTagging; 69 70constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1}; 71constexpr uintptr_t kUintptrAllBitsSet = 72 static_cast<uintptr_t>(kIntptrAllBitsSet); 73 74// Smi constants for systems where tagged pointer is a 32-bit value. 75template <> 76struct SmiTagging<4> { 77 enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; 78 79 static constexpr intptr_t kSmiMinValue = 80 static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); 81 static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); 82 83 V8_INLINE static int SmiToInt(Address value) { 84 int shift_bits = kSmiTagSize + kSmiShiftSize; 85 // Truncate and shift down (requires >> to be sign extending). 86 return static_cast<int32_t>(static_cast<uint32_t>(value)) >> shift_bits; 87 } 88 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 89 // Is value in range [kSmiMinValue, kSmiMaxValue]. 90 // Use unsigned operations in order to avoid undefined behaviour in case of 91 // signed integer overflow. 92 return (static_cast<uintptr_t>(value) - 93 static_cast<uintptr_t>(kSmiMinValue)) <= 94 (static_cast<uintptr_t>(kSmiMaxValue) - 95 static_cast<uintptr_t>(kSmiMinValue)); 96 } 97}; 98 99// Smi constants for systems where tagged pointer is a 64-bit value. 100template <> 101struct SmiTagging<8> { 102 enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; 103 104 static constexpr intptr_t kSmiMinValue = 105 static_cast<intptr_t>(kUintptrAllBitsSet << (kSmiValueSize - 1)); 106 static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); 107 108 V8_INLINE static int SmiToInt(Address value) { 109 int shift_bits = kSmiTagSize + kSmiShiftSize; 110 // Shift down and throw away top 32 bits. 111 return static_cast<int>(static_cast<intptr_t>(value) >> shift_bits); 112 } 113 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 114 // To be representable as a long smi, the value must be a 32-bit integer. 115 return (value == static_cast<int32_t>(value)); 116 } 117}; 118 119#ifdef V8_COMPRESS_POINTERS 120// See v8:7703 or src/common/ptr-compr-inl.h for details about pointer 121// compression. 122constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32; 123constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32; 124 125static_assert( 126 kApiSystemPointerSize == kApiInt64Size, 127 "Pointer compression can be enabled only for 64-bit architectures"); 128const int kApiTaggedSize = kApiInt32Size; 129#else 130const int kApiTaggedSize = kApiSystemPointerSize; 131#endif 132 133constexpr bool PointerCompressionIsEnabled() { 134 return kApiTaggedSize != kApiSystemPointerSize; 135} 136 137#ifdef V8_31BIT_SMIS_ON_64BIT_ARCH 138using PlatformSmiTagging = SmiTagging<kApiInt32Size>; 139#else 140using PlatformSmiTagging = SmiTagging<kApiTaggedSize>; 141#endif 142 143// TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize 144// since it's used much more often than the inividual constants. 145const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; 146const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; 147const int kSmiMinValue = static_cast<int>(PlatformSmiTagging::kSmiMinValue); 148const int kSmiMaxValue = static_cast<int>(PlatformSmiTagging::kSmiMaxValue); 149constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; } 150constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; } 151constexpr bool Is64() { return kApiSystemPointerSize == sizeof(int64_t); } 152 153V8_INLINE static constexpr Address IntToSmi(int value) { 154 return (static_cast<Address>(value) << (kSmiTagSize + kSmiShiftSize)) | 155 kSmiTag; 156} 157 158/* 159 * Sandbox related types, constants, and functions. 160 */ 161constexpr bool SandboxIsEnabled() { 162#ifdef V8_ENABLE_SANDBOX 163 return true; 164#else 165 return false; 166#endif 167} 168 169// SandboxedPointers are guaranteed to point into the sandbox. This is achieved 170// for example by storing them as offset rather than as raw pointers. 171using SandboxedPointer_t = Address; 172 173#ifdef V8_ENABLE_SANDBOX 174 175// Size of the sandbox, excluding the guard regions surrounding it. 176#ifdef V8_TARGET_OS_ANDROID 177// On Android, most 64-bit devices seem to be configured with only 39 bits of 178// virtual address space for userspace. As such, limit the sandbox to 128GB (a 179// quarter of the total available address space). 180constexpr size_t kSandboxSizeLog2 = 37; // 128 GB 181#else 182// Everywhere else use a 1TB sandbox. 183constexpr size_t kSandboxSizeLog2 = 40; // 1 TB 184#endif // V8_TARGET_OS_ANDROID 185constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2; 186 187// Required alignment of the sandbox. For simplicity, we require the 188// size of the guard regions to be a multiple of this, so that this specifies 189// the alignment of the sandbox including and excluding surrounding guard 190// regions. The alignment requirement is due to the pointer compression cage 191// being located at the start of the sandbox. 192constexpr size_t kSandboxAlignment = kPtrComprCageBaseAlignment; 193 194// Sandboxed pointers are stored inside the heap as offset from the sandbox 195// base shifted to the left. This way, it is guaranteed that the offset is 196// smaller than the sandbox size after shifting it to the right again. This 197// constant specifies the shift amount. 198constexpr uint64_t kSandboxedPointerShift = 64 - kSandboxSizeLog2; 199 200// Size of the guard regions surrounding the sandbox. This assumes a worst-case 201// scenario of a 32-bit unsigned index used to access an array of 64-bit 202// values. 203constexpr size_t kSandboxGuardRegionSize = 32ULL * GB; 204 205static_assert((kSandboxGuardRegionSize % kSandboxAlignment) == 0, 206 "The size of the guard regions around the sandbox must be a " 207 "multiple of its required alignment."); 208 209// On OSes where reserving virtual memory is too expensive to reserve the 210// entire address space backing the sandbox, notably Windows pre 8.1, we create 211// a partially reserved sandbox that doesn't actually reserve most of the 212// memory, and so doesn't have the desired security properties as unrelated 213// memory allocations could end up inside of it, but which still ensures that 214// objects that should be located inside the sandbox are allocated within 215// kSandboxSize bytes from the start of the sandbox. The minimum size of the 216// region that is actually reserved for such a sandbox is specified by this 217// constant and should be big enough to contain the pointer compression cage as 218// well as the ArrayBuffer partition. 219constexpr size_t kSandboxMinimumReservationSize = 8ULL * GB; 220 221static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize, 222 "The minimum reservation size for a sandbox must be larger than " 223 "the pointer compression cage contained within it."); 224 225// The maximum buffer size allowed inside the sandbox. This is mostly dependent 226// on the size of the guard regions around the sandbox: an attacker must not be 227// able to construct a buffer that appears larger than the guard regions and 228// thereby "reach out of" the sandbox. 229constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1; 230static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize, 231 "The maximum allowed buffer size must not be larger than the " 232 "sandbox's guard regions"); 233 234constexpr size_t kBoundedSizeShift = 29; 235static_assert(1ULL << (64 - kBoundedSizeShift) == 236 kMaxSafeBufferSizeForSandbox + 1, 237 "The maximum size of a BoundedSize must be synchronized with the " 238 "kMaxSafeBufferSizeForSandbox"); 239 240#endif // V8_ENABLE_SANDBOX 241 242#ifdef V8_COMPRESS_POINTERS 243 244#ifdef V8_TARGET_OS_ANDROID 245// The size of the virtual memory reservation for an external pointer table. 246// This determines the maximum number of entries in a table. Using a maximum 247// size allows omitting bounds checks on table accesses if the indices are 248// guaranteed (e.g. through shifting) to be below the maximum index. This 249// value must be a power of two. 250static const size_t kExternalPointerTableReservationSize = 512 * MB; 251 252// The external pointer table indices stored in HeapObjects as external 253// pointers are shifted to the left by this amount to guarantee that they are 254// smaller than the maximum table size. 255static const uint32_t kExternalPointerIndexShift = 6; 256#else 257static const size_t kExternalPointerTableReservationSize = 1024 * MB; 258static const uint32_t kExternalPointerIndexShift = 5; 259#endif // V8_TARGET_OS_ANDROID 260 261// The maximum number of entries in an external pointer table. 262static const size_t kMaxExternalPointers = 263 kExternalPointerTableReservationSize / kApiSystemPointerSize; 264static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers, 265 "kExternalPointerTableReservationSize and " 266 "kExternalPointerIndexShift don't match"); 267 268#else // !V8_COMPRESS_POINTERS 269 270// Needed for the V8.SandboxedExternalPointersCount histogram. 271static const size_t kMaxExternalPointers = 0; 272 273#endif // V8_COMPRESS_POINTERS 274 275// A ExternalPointerHandle represents a (opaque) reference to an external 276// pointer that can be stored inside the sandbox. A ExternalPointerHandle has 277// meaning only in combination with an (active) Isolate as it references an 278// external pointer stored in the currently active Isolate's 279// ExternalPointerTable. Internally, an ExternalPointerHandles is simply an 280// index into an ExternalPointerTable that is shifted to the left to guarantee 281// that it is smaller than the size of the table. 282using ExternalPointerHandle = uint32_t; 283 284// ExternalPointers point to objects located outside the sandbox. When 285// sandboxed external pointers are enabled, these are stored on heap as 286// ExternalPointerHandles, otherwise they are simply raw pointers. 287#ifdef V8_ENABLE_SANDBOX 288using ExternalPointer_t = ExternalPointerHandle; 289#else 290using ExternalPointer_t = Address; 291#endif 292 293// When the sandbox is enabled, external pointers are stored in an external 294// pointer table and are referenced from HeapObjects through an index (a 295// "handle"). When stored in the table, the pointers are tagged with per-type 296// tags to prevent type confusion attacks between different external objects. 297// Besides type information bits, these tags also contain the GC marking bit 298// which indicates whether the pointer table entry is currently alive. When a 299// pointer is written into the table, the tag is ORed into the top bits. When 300// that pointer is later loaded from the table, it is ANDed with the inverse of 301// the expected tag. If the expected and actual type differ, this will leave 302// some of the top bits of the pointer set, rendering the pointer inaccessible. 303// The AND operation also removes the GC marking bit from the pointer. 304// 305// The tags are constructed such that UNTAG(TAG(0, T1), T2) != 0 for any two 306// (distinct) tags T1 and T2. In practice, this is achieved by generating tags 307// that all have the same number of zeroes and ones but different bit patterns. 308// With N type tag bits, this allows for (N choose N/2) possible type tags. 309// Besides the type tag bits, the tags also have the GC marking bit set so that 310// the marking bit is automatically set when a pointer is written into the 311// external pointer table (in which case it is clearly alive) and is cleared 312// when the pointer is loaded. The exception to this is the free entry tag, 313// which doesn't have the mark bit set, as the entry is not alive. This 314// construction allows performing the type check and removing GC marking bits 315// from the pointer in one efficient operation (bitwise AND). The number of 316// available bits is limited in the following way: on x64, bits [47, 64) are 317// generally available for tagging (userspace has 47 address bits available). 318// On Arm64, userspace typically has a 40 or 48 bit address space. However, due 319// to top-byte ignore (TBI) and memory tagging (MTE), the top byte is unusable 320// for type checks as type-check failures would go unnoticed or collide with 321// MTE bits. Some bits of the top byte can, however, still be used for the GC 322// marking bit. The bits available for the type tags are therefore limited to 323// [48, 56), i.e. (8 choose 4) = 70 different types. 324// The following options exist to increase the number of possible types: 325// - Using multiple ExternalPointerTables since tags can safely be reused 326// across different tables 327// - Using "extended" type checks, where additional type information is stored 328// either in an adjacent pointer table entry or at the pointed-to location 329// - Using a different tagging scheme, for example based on XOR which would 330// allow for 2**8 different tags but require a separate operation to remove 331// the marking bit 332// 333// The external pointer sandboxing mechanism ensures that every access to an 334// external pointer field will result in a valid pointer of the expected type 335// even in the presence of an attacker able to corrupt memory inside the 336// sandbox. However, if any data related to the external object is stored 337// inside the sandbox it may still be corrupted and so must be validated before 338// use or moved into the external object. Further, an attacker will always be 339// able to substitute different external pointers of the same type for each 340// other. Therefore, code using external pointers must be written in a 341// "substitution-safe" way, i.e. it must always be possible to substitute 342// external pointers of the same type without causing memory corruption outside 343// of the sandbox. Generally this is achieved by referencing any group of 344// related external objects through a single external pointer. 345// 346// Currently we use bit 62 for the marking bit which should always be unused as 347// it's part of the non-canonical address range. When Arm's top-byte ignore 348// (TBI) is enabled, this bit will be part of the ignored byte, and we assume 349// that the Embedder is not using this byte (really only this one bit) for any 350// other purpose. This bit also does not collide with the memory tagging 351// extension (MTE) which would use bits [56, 60). 352// 353// External pointer tables are also available even when the sandbox is off but 354// pointer compression is on. In that case, the mechanism can be used to easy 355// alignment requirements as it turns unaligned 64-bit raw pointers into 356// aligned 32-bit indices. To "opt-in" to the external pointer table mechanism 357// for this purpose, instead of using the ExternalPointer accessors one needs to 358// use ExternalPointerHandles directly and use them to access the pointers in an 359// ExternalPointerTable. 360constexpr uint64_t kExternalPointerMarkBit = 1ULL << 62; 361constexpr uint64_t kExternalPointerTagMask = 0x40ff000000000000; 362constexpr uint64_t kExternalPointerTagShift = 48; 363 364// All possible 8-bit type tags. 365// These are sorted so that tags can be grouped together and it can efficiently 366// be checked if a tag belongs to a given group. See for example the 367// IsSharedExternalPointerType routine. 368constexpr uint64_t kAllExternalPointerTypeTags[] = { 369 0b00001111, 0b00010111, 0b00011011, 0b00011101, 0b00011110, 0b00100111, 370 0b00101011, 0b00101101, 0b00101110, 0b00110011, 0b00110101, 0b00110110, 371 0b00111001, 0b00111010, 0b00111100, 0b01000111, 0b01001011, 0b01001101, 372 0b01001110, 0b01010011, 0b01010101, 0b01010110, 0b01011001, 0b01011010, 373 0b01011100, 0b01100011, 0b01100101, 0b01100110, 0b01101001, 0b01101010, 374 0b01101100, 0b01110001, 0b01110010, 0b01110100, 0b01111000, 0b10000111, 375 0b10001011, 0b10001101, 0b10001110, 0b10010011, 0b10010101, 0b10010110, 376 0b10011001, 0b10011010, 0b10011100, 0b10100011, 0b10100101, 0b10100110, 377 0b10101001, 0b10101010, 0b10101100, 0b10110001, 0b10110010, 0b10110100, 378 0b10111000, 0b11000011, 0b11000101, 0b11000110, 0b11001001, 0b11001010, 379 0b11001100, 0b11010001, 0b11010010, 0b11010100, 0b11011000, 0b11100001, 380 0b11100010, 0b11100100, 0b11101000, 0b11110000}; 381 382#define TAG(i) \ 383 ((kAllExternalPointerTypeTags[i] << kExternalPointerTagShift) | \ 384 kExternalPointerMarkBit) 385 386// clang-format off 387 388// When adding new tags, please ensure that the code using these tags is 389// "substitution-safe", i.e. still operate safely if external pointers of the 390// same type are swapped by an attacker. See comment above for more details. 391 392// Shared external pointers are owned by the shared Isolate and stored in the 393// shared external pointer table associated with that Isolate, where they can 394// be accessed from multiple threads at the same time. The objects referenced 395// in this way must therefore always be thread-safe. 396#define SHARED_EXTERNAL_POINTER_TAGS(V) \ 397 V(kFirstSharedTag, TAG(0)) \ 398 V(kWaiterQueueNodeTag, TAG(0)) \ 399 V(kExternalStringResourceTag, TAG(1)) \ 400 V(kExternalStringResourceDataTag, TAG(2)) \ 401 V(kLastSharedTag, TAG(2)) 402 403// External pointers using these tags are kept in a per-Isolate external 404// pointer table and can only be accessed when this Isolate is active. 405#define PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) \ 406 V(kForeignForeignAddressTag, TAG(10)) \ 407 V(kNativeContextMicrotaskQueueTag, TAG(11)) \ 408 V(kEmbedderDataSlotPayloadTag, TAG(12)) \ 409/* This tag essentially stands for a `void*` pointer in the V8 API, and */ \ 410/* it is the Embedder's responsibility to ensure type safety (against */ \ 411/* substitution) and lifetime validity of these objects. */ \ 412 V(kExternalObjectValueTag, TAG(13)) \ 413 V(kCallHandlerInfoCallbackTag, TAG(14)) \ 414 V(kAccessorInfoGetterTag, TAG(15)) \ 415 V(kAccessorInfoSetterTag, TAG(16)) \ 416 V(kWasmInternalFunctionCallTargetTag, TAG(17)) \ 417 V(kWasmTypeInfoNativeTypeTag, TAG(18)) \ 418 V(kWasmExportedFunctionDataSignatureTag, TAG(19)) \ 419 V(kWasmContinuationJmpbufTag, TAG(20)) \ 420 V(kArrayBufferExtensionTag, TAG(21)) 421 422// All external pointer tags. 423#define ALL_EXTERNAL_POINTER_TAGS(V) \ 424 SHARED_EXTERNAL_POINTER_TAGS(V) \ 425 PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) 426 427#define EXTERNAL_POINTER_TAG_ENUM(Name, Tag) Name = Tag, 428#define MAKE_TAG(HasMarkBit, TypeTag) \ 429 ((static_cast<uint64_t>(TypeTag) << kExternalPointerTagShift) | \ 430 (HasMarkBit ? kExternalPointerMarkBit : 0)) 431enum ExternalPointerTag : uint64_t { 432 // Empty tag value. Mostly used as placeholder. 433 kExternalPointerNullTag = MAKE_TAG(0, 0b00000000), 434 // External pointer tag that will match any external pointer. Use with care! 435 kAnyExternalPointerTag = MAKE_TAG(1, 0b11111111), 436 // The free entry tag has all type bits set so every type check with a 437 // different type fails. It also doesn't have the mark bit set as free 438 // entries are (by definition) not alive. 439 kExternalPointerFreeEntryTag = MAKE_TAG(0, 0b11111111), 440 // Evacuation entries are used during external pointer table compaction. 441 kExternalPointerEvacuationEntryTag = MAKE_TAG(1, 0b11100111), 442 443 ALL_EXTERNAL_POINTER_TAGS(EXTERNAL_POINTER_TAG_ENUM) 444}; 445 446#undef MAKE_TAG 447#undef TAG 448#undef EXTERNAL_POINTER_TAG_ENUM 449 450// clang-format on 451 452// True if the external pointer must be accessed from the shared isolate's 453// external pointer table. 454V8_INLINE static constexpr bool IsSharedExternalPointerType( 455 ExternalPointerTag tag) { 456 return tag >= kFirstSharedTag && tag <= kLastSharedTag; 457} 458 459// Sanity checks. 460#define CHECK_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ 461 static_assert(IsSharedExternalPointerType(Tag)); 462#define CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ 463 static_assert(!IsSharedExternalPointerType(Tag)); 464 465SHARED_EXTERNAL_POINTER_TAGS(CHECK_SHARED_EXTERNAL_POINTER_TAGS) 466PER_ISOLATE_EXTERNAL_POINTER_TAGS(CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS) 467 468#undef CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS 469#undef CHECK_SHARED_EXTERNAL_POINTER_TAGS 470 471#undef SHARED_EXTERNAL_POINTER_TAGS 472#undef EXTERNAL_POINTER_TAGS 473 474// {obj} must be the raw tagged pointer representation of a HeapObject 475// that's guaranteed to never be in ReadOnlySpace. 476V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); 477 478// Returns if we need to throw when an error occurs. This infers the language 479// mode based on the current context and the closure. This returns true if the 480// language mode is strict. 481V8_EXPORT bool ShouldThrowOnError(internal::Isolate* isolate); 482/** 483 * This class exports constants and functionality from within v8 that 484 * is necessary to implement inline functions in the v8 api. Don't 485 * depend on functions and constants defined here. 486 */ 487class Internals { 488#ifdef V8_MAP_PACKING 489 V8_INLINE static constexpr Address UnpackMapWord(Address mapword) { 490 // TODO(wenyuzhao): Clear header metadata. 491 return mapword ^ kMapWordXorMask; 492 } 493#endif 494 495 public: 496 // These values match non-compiler-dependent values defined within 497 // the implementation of v8. 498 static const int kHeapObjectMapOffset = 0; 499 static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size; 500 static const int kStringResourceOffset = 501 1 * kApiTaggedSize + 2 * kApiInt32Size; 502 503 static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; 504 static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; 505 static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; 506 static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; 507 static const int kEmbedderDataSlotSize = kApiSystemPointerSize; 508#ifdef V8_ENABLE_SANDBOX 509 static const int kEmbedderDataSlotExternalPointerOffset = kApiTaggedSize; 510#else 511 static const int kEmbedderDataSlotExternalPointerOffset = 0; 512#endif 513 static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; 514 static const int kStringRepresentationAndEncodingMask = 0x0f; 515 static const int kStringEncodingMask = 0x8; 516 static const int kExternalTwoByteRepresentationTag = 0x02; 517 static const int kExternalOneByteRepresentationTag = 0x0a; 518 519 static const uint32_t kNumIsolateDataSlots = 4; 520 static const int kStackGuardSize = 7 * kApiSystemPointerSize; 521 static const int kBuiltinTier0EntryTableSize = 7 * kApiSystemPointerSize; 522 static const int kBuiltinTier0TableSize = 7 * kApiSystemPointerSize; 523 static const int kLinearAllocationAreaSize = 3 * kApiSystemPointerSize; 524 static const int kThreadLocalTopSize = 25 * kApiSystemPointerSize; 525 static const int kHandleScopeDataSize = 526 2 * kApiSystemPointerSize + 2 * kApiInt32Size; 527 528 // ExternalPointerTable layout guarantees. 529 static const int kExternalPointerTableBufferOffset = 0; 530 static const int kExternalPointerTableSize = 4 * kApiSystemPointerSize; 531 532 // IsolateData layout guarantees. 533 static const int kIsolateCageBaseOffset = 0; 534 static const int kIsolateStackGuardOffset = 535 kIsolateCageBaseOffset + kApiSystemPointerSize; 536 static const int kVariousBooleanFlagsOffset = 537 kIsolateStackGuardOffset + kStackGuardSize; 538 static const int kBuiltinTier0EntryTableOffset = 539 kVariousBooleanFlagsOffset + 8; 540 static const int kBuiltinTier0TableOffset = 541 kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize; 542 static const int kNewAllocationInfoOffset = 543 kBuiltinTier0TableOffset + kBuiltinTier0TableSize; 544 static const int kOldAllocationInfoOffset = 545 kNewAllocationInfoOffset + kLinearAllocationAreaSize; 546 static const int kIsolateFastCCallCallerFpOffset = 547 kOldAllocationInfoOffset + kLinearAllocationAreaSize; 548 static const int kIsolateFastCCallCallerPcOffset = 549 kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; 550 static const int kIsolateFastApiCallTargetOffset = 551 kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; 552 static const int kIsolateLongTaskStatsCounterOffset = 553 kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; 554 static const int kIsolateThreadLocalTopOffset = 555 kIsolateLongTaskStatsCounterOffset + kApiSizetSize; 556 static const int kIsolateHandleScopeDataOffset = 557 kIsolateThreadLocalTopOffset + kThreadLocalTopSize; 558 static const int kIsolateEmbedderDataOffset = 559 kIsolateHandleScopeDataOffset + kHandleScopeDataSize; 560#ifdef V8_COMPRESS_POINTERS 561 static const int kIsolateExternalPointerTableOffset = 562 kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; 563 static const int kIsolateSharedExternalPointerTableAddressOffset = 564 kIsolateExternalPointerTableOffset + kExternalPointerTableSize; 565 static const int kIsolateRootsOffset = 566 kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize; 567#else 568 static const int kIsolateRootsOffset = 569 kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; 570#endif 571 572#if V8_STATIC_ROOTS_BOOL 573 574// These constants need to be initialized in api.cc. 575#define EXPORTED_STATIC_ROOTS_PTR_LIST(V) \ 576 V(UndefinedValue) \ 577 V(NullValue) \ 578 V(TrueValue) \ 579 V(FalseValue) \ 580 V(EmptyString) \ 581 V(TheHoleValue) 582 583 using Tagged_t = uint32_t; 584 struct StaticReadOnlyRoot { 585#define DEF_ROOT(name) V8_EXPORT static const Tagged_t k##name; 586 EXPORTED_STATIC_ROOTS_PTR_LIST(DEF_ROOT) 587#undef DEF_ROOT 588 589 V8_EXPORT static const Tagged_t kFirstStringMap; 590 V8_EXPORT static const Tagged_t kLastStringMap; 591 }; 592 593#endif // V8_STATIC_ROOTS_BOOL 594 595 static const int kUndefinedValueRootIndex = 4; 596 static const int kTheHoleValueRootIndex = 5; 597 static const int kNullValueRootIndex = 6; 598 static const int kTrueValueRootIndex = 7; 599 static const int kFalseValueRootIndex = 8; 600 static const int kEmptyStringRootIndex = 9; 601 602 static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; 603 static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; 604 static const int kNodeStateMask = 0x3; 605 static const int kNodeStateIsWeakValue = 2; 606 607 static const int kTracedNodeClassIdOffset = kApiSystemPointerSize; 608 609 static const int kFirstNonstringType = 0x80; 610 static const int kOddballType = 0x83; 611 static const int kForeignType = 0xcc; 612 static const int kJSSpecialApiObjectType = 0x410; 613 static const int kJSObjectType = 0x421; 614 static const int kFirstJSApiObjectType = 0x422; 615 static const int kLastJSApiObjectType = 0x80A; 616 617 static const int kUndefinedOddballKind = 5; 618 static const int kNullOddballKind = 3; 619 620 // Constants used by PropertyCallbackInfo to check if we should throw when an 621 // error occurs. 622 static const int kThrowOnError = 0; 623 static const int kDontThrow = 1; 624 static const int kInferShouldThrowMode = 2; 625 626 // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an 627 // incremental GC once the external memory reaches this limit. 628 static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024; 629 630#ifdef V8_MAP_PACKING 631 static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48; 632 // The lowest two bits of mapwords are always `0b10` 633 static const uintptr_t kMapWordSignature = 0b10; 634 // XORing a (non-compressed) map with this mask ensures that the two 635 // low-order bits are 0b10. The 0 at the end makes this look like a Smi, 636 // although real Smis have all lower 32 bits unset. We only rely on these 637 // values passing as Smis in very few places. 638 static const int kMapWordXorMask = 0b11; 639#endif 640 641 V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); 642 V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { 643#ifdef V8_ENABLE_CHECKS 644 CheckInitializedImpl(isolate); 645#endif 646 } 647 648 V8_INLINE static bool HasHeapObjectTag(Address value) { 649 return (value & kHeapObjectTagMask) == static_cast<Address>(kHeapObjectTag); 650 } 651 652 V8_INLINE static int SmiValue(Address value) { 653 return PlatformSmiTagging::SmiToInt(value); 654 } 655 656 V8_INLINE static constexpr Address IntToSmi(int value) { 657 return internal::IntToSmi(value); 658 } 659 660 V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { 661 return PlatformSmiTagging::IsValidSmi(value); 662 } 663 664#if V8_STATIC_ROOTS_BOOL 665 V8_INLINE static bool is_identical(Address obj, Tagged_t constant) { 666 return static_cast<Tagged_t>(obj) == constant; 667 } 668 669 V8_INLINE static bool CheckInstanceMapRange(Address obj, Tagged_t first_map, 670 Tagged_t last_map) { 671 auto map = ReadRawField<Tagged_t>(obj, kHeapObjectMapOffset); 672#ifdef V8_MAP_PACKING 673 map = UnpackMapWord(map); 674#endif 675 return map >= first_map && map <= last_map; 676 } 677#endif 678 679 V8_INLINE static int GetInstanceType(Address obj) { 680 Address map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); 681#ifdef V8_MAP_PACKING 682 map = UnpackMapWord(map); 683#endif 684 return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset); 685 } 686 687 V8_INLINE static int GetOddballKind(Address obj) { 688 return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset)); 689 } 690 691 V8_INLINE static bool IsExternalTwoByteString(int instance_type) { 692 int representation = (instance_type & kStringRepresentationAndEncodingMask); 693 return representation == kExternalTwoByteRepresentationTag; 694 } 695 696 V8_INLINE static constexpr bool CanHaveInternalField(int instance_type) { 697 static_assert(kJSObjectType + 1 == kFirstJSApiObjectType); 698 static_assert(kJSObjectType < kLastJSApiObjectType); 699 static_assert(kFirstJSApiObjectType < kLastJSApiObjectType); 700 // Check for IsJSObject() || IsJSSpecialApiObject() || IsJSApiObject() 701 return instance_type == kJSSpecialApiObjectType || 702 // inlined version of base::IsInRange 703 (static_cast<unsigned>(static_cast<unsigned>(instance_type) - 704 static_cast<unsigned>(kJSObjectType)) <= 705 static_cast<unsigned>(kLastJSApiObjectType - kJSObjectType)); 706 } 707 708 V8_INLINE static uint8_t GetNodeFlag(Address* obj, int shift) { 709 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 710 return *addr & static_cast<uint8_t>(1U << shift); 711 } 712 713 V8_INLINE static void UpdateNodeFlag(Address* obj, bool value, int shift) { 714 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 715 uint8_t mask = static_cast<uint8_t>(1U << shift); 716 *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift)); 717 } 718 719 V8_INLINE static uint8_t GetNodeState(Address* obj) { 720 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 721 return *addr & kNodeStateMask; 722 } 723 724 V8_INLINE static void UpdateNodeState(Address* obj, uint8_t value) { 725 uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset; 726 *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value); 727 } 728 729 V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot, 730 void* data) { 731 Address addr = reinterpret_cast<Address>(isolate) + 732 kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize; 733 *reinterpret_cast<void**>(addr) = data; 734 } 735 736 V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate, 737 uint32_t slot) { 738 Address addr = reinterpret_cast<Address>(isolate) + 739 kIsolateEmbedderDataOffset + slot * kApiSystemPointerSize; 740 return *reinterpret_cast<void* const*>(addr); 741 } 742 743 V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) { 744 Address addr = 745 reinterpret_cast<Address>(isolate) + kIsolateLongTaskStatsCounterOffset; 746 ++(*reinterpret_cast<size_t*>(addr)); 747 } 748 749 V8_INLINE static Address* GetRootSlot(v8::Isolate* isolate, int index) { 750 Address addr = reinterpret_cast<Address>(isolate) + kIsolateRootsOffset + 751 index * kApiSystemPointerSize; 752 return reinterpret_cast<Address*>(addr); 753 } 754 755 V8_INLINE static Address GetRoot(v8::Isolate* isolate, int index) { 756#if V8_STATIC_ROOTS_BOOL 757 Address base = *reinterpret_cast<Address*>( 758 reinterpret_cast<uintptr_t>(isolate) + kIsolateCageBaseOffset); 759 switch (index) { 760#define DECOMPRESS_ROOT(name) \ 761 case k##name##RootIndex: \ 762 return base + StaticReadOnlyRoot::k##name; 763 EXPORTED_STATIC_ROOTS_PTR_LIST(DECOMPRESS_ROOT) 764#undef DECOMPRESS_ROOT 765 default: 766 break; 767 } 768#undef EXPORTED_STATIC_ROOTS_PTR_LIST 769#endif // V8_STATIC_ROOTS_BOOL 770 return *GetRootSlot(isolate, index); 771 } 772 773#ifdef V8_ENABLE_SANDBOX 774 V8_INLINE static Address* GetExternalPointerTableBase(v8::Isolate* isolate) { 775 Address addr = reinterpret_cast<Address>(isolate) + 776 kIsolateExternalPointerTableOffset + 777 kExternalPointerTableBufferOffset; 778 return *reinterpret_cast<Address**>(addr); 779 } 780 781 V8_INLINE static Address* GetSharedExternalPointerTableBase( 782 v8::Isolate* isolate) { 783 Address addr = reinterpret_cast<Address>(isolate) + 784 kIsolateSharedExternalPointerTableAddressOffset; 785 addr = *reinterpret_cast<Address*>(addr); 786 addr += kExternalPointerTableBufferOffset; 787 return *reinterpret_cast<Address**>(addr); 788 } 789#endif 790 791 template <typename T> 792 V8_INLINE static T ReadRawField(Address heap_object_ptr, int offset) { 793 Address addr = heap_object_ptr + offset - kHeapObjectTag; 794#ifdef V8_COMPRESS_POINTERS 795 if (sizeof(T) > kApiTaggedSize) { 796 // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size 797 // fields (external pointers, doubles and BigInt data) are only 798 // kTaggedSize aligned so we have to use unaligned pointer friendly way of 799 // accessing them in order to avoid undefined behavior in C++ code. 800 T r; 801 memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T)); 802 return r; 803 } 804#endif 805 return *reinterpret_cast<const T*>(addr); 806 } 807 808 V8_INLINE static Address ReadTaggedPointerField(Address heap_object_ptr, 809 int offset) { 810#ifdef V8_COMPRESS_POINTERS 811 uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset); 812 Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); 813 return base + static_cast<Address>(static_cast<uintptr_t>(value)); 814#else 815 return ReadRawField<Address>(heap_object_ptr, offset); 816#endif 817 } 818 819 V8_INLINE static Address ReadTaggedSignedField(Address heap_object_ptr, 820 int offset) { 821#ifdef V8_COMPRESS_POINTERS 822 uint32_t value = ReadRawField<uint32_t>(heap_object_ptr, offset); 823 return static_cast<Address>(static_cast<uintptr_t>(value)); 824#else 825 return ReadRawField<Address>(heap_object_ptr, offset); 826#endif 827 } 828 829 V8_INLINE static v8::Isolate* GetIsolateForSandbox(Address obj) { 830#ifdef V8_ENABLE_SANDBOX 831 return reinterpret_cast<v8::Isolate*>( 832 internal::IsolateFromNeverReadOnlySpaceObject(obj)); 833#else 834 // Not used in non-sandbox mode. 835 return nullptr; 836#endif 837 } 838 839 template <ExternalPointerTag tag> 840 V8_INLINE static Address ReadExternalPointerField(v8::Isolate* isolate, 841 Address heap_object_ptr, 842 int offset) { 843#ifdef V8_ENABLE_SANDBOX 844 static_assert(tag != kExternalPointerNullTag); 845 // See src/sandbox/external-pointer-table-inl.h. Logic duplicated here so 846 // it can be inlined and doesn't require an additional call. 847 Address* table = IsSharedExternalPointerType(tag) 848 ? GetSharedExternalPointerTableBase(isolate) 849 : GetExternalPointerTableBase(isolate); 850 internal::ExternalPointerHandle handle = 851 ReadRawField<ExternalPointerHandle>(heap_object_ptr, offset); 852 uint32_t index = handle >> kExternalPointerIndexShift; 853 std::atomic<Address>* ptr = 854 reinterpret_cast<std::atomic<Address>*>(&table[index]); 855 Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed); 856 return entry & ~tag; 857#else 858 return ReadRawField<Address>(heap_object_ptr, offset); 859#endif // V8_ENABLE_SANDBOX 860 } 861 862#ifdef V8_COMPRESS_POINTERS 863 V8_INLINE static Address GetPtrComprCageBaseFromOnHeapAddress(Address addr) { 864 return addr & -static_cast<intptr_t>(kPtrComprCageBaseAlignment); 865 } 866 867 V8_INLINE static Address DecompressTaggedField(Address heap_object_ptr, 868 uint32_t value) { 869 Address base = GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); 870 return base + static_cast<Address>(static_cast<uintptr_t>(value)); 871 } 872 873#endif // V8_COMPRESS_POINTERS 874}; 875 876// Only perform cast check for types derived from v8::Data since 877// other types do not implement the Cast method. 878template <bool PerformCheck> 879struct CastCheck { 880 template <class T> 881 static void Perform(T* data); 882}; 883 884template <> 885template <class T> 886void CastCheck<true>::Perform(T* data) { 887 T::Cast(data); 888} 889 890template <> 891template <class T> 892void CastCheck<false>::Perform(T* data) {} 893 894template <class T> 895V8_INLINE void PerformCastCheck(T* data) { 896 CastCheck<std::is_base_of<Data, T>::value && 897 !std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data); 898} 899 900// A base class for backing stores, which is needed due to vagaries of 901// how static casts work with std::shared_ptr. 902class BackingStoreBase {}; 903 904// The maximum value in enum GarbageCollectionReason, defined in heap.h. 905// This is needed for histograms sampling garbage collection reasons. 906constexpr int kGarbageCollectionReasonMaxValue = 27; 907 908} // namespace internal 909} // namespace v8 910 911#endif // INCLUDE_V8_INTERNAL_H_ 912