xref: /third_party/node/deps/v8/src/heap/code-range.cc (revision 1cb0ef41)
11cb0ef41Sopenharmony_ci// Copyright 2021 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/heap/code-range.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/base/bits.h"
81cb0ef41Sopenharmony_ci#include "src/base/lazy-instance.h"
91cb0ef41Sopenharmony_ci#include "src/common/globals.h"
101cb0ef41Sopenharmony_ci#include "src/flags/flags.h"
111cb0ef41Sopenharmony_ci#include "src/heap/heap-inl.h"
121cb0ef41Sopenharmony_ci#include "src/utils/allocation.h"
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_cinamespace v8 {
151cb0ef41Sopenharmony_cinamespace internal {
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_cinamespace {
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci// Mutex for creating process_wide_code_range_.
201cb0ef41Sopenharmony_cibase::LazyMutex process_wide_code_range_creation_mutex_ =
211cb0ef41Sopenharmony_ci    LAZY_MUTEX_INITIALIZER;
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ci// Weak pointer holding the process-wide CodeRange, if one has been created. All
241cb0ef41Sopenharmony_ci// Heaps hold a std::shared_ptr to this, so this is destroyed when no Heaps
251cb0ef41Sopenharmony_ci// remain.
261cb0ef41Sopenharmony_cibase::LazyInstance<std::weak_ptr<CodeRange>>::type process_wide_code_range_ =
271cb0ef41Sopenharmony_ci    LAZY_INSTANCE_INITIALIZER;
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciDEFINE_LAZY_LEAKY_OBJECT_GETTER(CodeRangeAddressHint, GetCodeRangeAddressHint)
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_civoid FunctionInStaticBinaryForAddressHint() {}
321cb0ef41Sopenharmony_ci}  // anonymous namespace
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ciAddress CodeRangeAddressHint::GetAddressHint(size_t code_range_size,
351cb0ef41Sopenharmony_ci                                             size_t alignment) {
361cb0ef41Sopenharmony_ci  base::MutexGuard guard(&mutex_);
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  // Try to allocate code range in the preferred region where we can use
391cb0ef41Sopenharmony_ci  // short instructions for calling/jumping to embedded builtins.
401cb0ef41Sopenharmony_ci  base::AddressRegion preferred_region = Isolate::GetShortBuiltinsCallRegion();
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci  Address result = 0;
431cb0ef41Sopenharmony_ci  auto it = recently_freed_.find(code_range_size);
441cb0ef41Sopenharmony_ci  // No recently freed region has been found, try to provide a hint for placing
451cb0ef41Sopenharmony_ci  // a code region.
461cb0ef41Sopenharmony_ci  if (it == recently_freed_.end() || it->second.empty()) {
471cb0ef41Sopenharmony_ci    if (V8_ENABLE_NEAR_CODE_RANGE_BOOL && !preferred_region.is_empty()) {
481cb0ef41Sopenharmony_ci      auto memory_ranges = base::OS::GetFreeMemoryRangesWithin(
491cb0ef41Sopenharmony_ci          preferred_region.begin(), preferred_region.end(), code_range_size,
501cb0ef41Sopenharmony_ci          alignment);
511cb0ef41Sopenharmony_ci      if (!memory_ranges.empty()) {
521cb0ef41Sopenharmony_ci        result = memory_ranges.front().start;
531cb0ef41Sopenharmony_ci        CHECK(IsAligned(result, alignment));
541cb0ef41Sopenharmony_ci        return result;
551cb0ef41Sopenharmony_ci      }
561cb0ef41Sopenharmony_ci      // The empty memory_ranges means that GetFreeMemoryRangesWithin() API
571cb0ef41Sopenharmony_ci      // is not supported, so use the lowest address from the preferred region
581cb0ef41Sopenharmony_ci      // as a hint because it'll be at least as good as the fallback hint but
591cb0ef41Sopenharmony_ci      // with a higher chances to point to the free address space range.
601cb0ef41Sopenharmony_ci      return RoundUp(preferred_region.begin(), alignment);
611cb0ef41Sopenharmony_ci    }
621cb0ef41Sopenharmony_ci    return RoundUp(FUNCTION_ADDR(&FunctionInStaticBinaryForAddressHint),
631cb0ef41Sopenharmony_ci                   alignment);
641cb0ef41Sopenharmony_ci  }
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  // Try to reuse near code range first.
671cb0ef41Sopenharmony_ci  if (V8_ENABLE_NEAR_CODE_RANGE_BOOL && !preferred_region.is_empty()) {
681cb0ef41Sopenharmony_ci    auto freed_regions_for_size = it->second;
691cb0ef41Sopenharmony_ci    for (auto it_freed = freed_regions_for_size.rbegin();
701cb0ef41Sopenharmony_ci         it_freed != freed_regions_for_size.rend(); ++it_freed) {
711cb0ef41Sopenharmony_ci      Address code_range_start = *it_freed;
721cb0ef41Sopenharmony_ci      if (preferred_region.contains(code_range_start, code_range_size)) {
731cb0ef41Sopenharmony_ci        CHECK(IsAligned(code_range_start, alignment));
741cb0ef41Sopenharmony_ci        freed_regions_for_size.erase((it_freed + 1).base());
751cb0ef41Sopenharmony_ci        return code_range_start;
761cb0ef41Sopenharmony_ci      }
771cb0ef41Sopenharmony_ci    }
781cb0ef41Sopenharmony_ci  }
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci  result = it->second.back();
811cb0ef41Sopenharmony_ci  CHECK(IsAligned(result, alignment));
821cb0ef41Sopenharmony_ci  it->second.pop_back();
831cb0ef41Sopenharmony_ci  return result;
841cb0ef41Sopenharmony_ci}
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_civoid CodeRangeAddressHint::NotifyFreedCodeRange(Address code_range_start,
871cb0ef41Sopenharmony_ci                                                size_t code_range_size) {
881cb0ef41Sopenharmony_ci  base::MutexGuard guard(&mutex_);
891cb0ef41Sopenharmony_ci  recently_freed_[code_range_size].push_back(code_range_start);
901cb0ef41Sopenharmony_ci}
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ciCodeRange::~CodeRange() { Free(); }
931cb0ef41Sopenharmony_ci
941cb0ef41Sopenharmony_ci// static
951cb0ef41Sopenharmony_cisize_t CodeRange::GetWritableReservedAreaSize() {
961cb0ef41Sopenharmony_ci  return kReservedCodeRangePages * MemoryAllocator::GetCommitPageSize();
971cb0ef41Sopenharmony_ci}
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_cibool CodeRange::InitReservation(v8::PageAllocator* page_allocator,
1001cb0ef41Sopenharmony_ci                                size_t requested) {
1011cb0ef41Sopenharmony_ci  DCHECK_NE(requested, 0);
1021cb0ef41Sopenharmony_ci  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
1031cb0ef41Sopenharmony_ci    page_allocator = GetPlatformPageAllocator();
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci  if (requested <= kMinimumCodeRangeSize) {
1071cb0ef41Sopenharmony_ci    requested = kMinimumCodeRangeSize;
1081cb0ef41Sopenharmony_ci  }
1091cb0ef41Sopenharmony_ci  const size_t reserved_area = GetWritableReservedAreaSize();
1101cb0ef41Sopenharmony_ci  if (requested < (kMaximalCodeRangeSize - reserved_area)) {
1111cb0ef41Sopenharmony_ci    requested += RoundUp(reserved_area, MemoryChunk::kPageSize);
1121cb0ef41Sopenharmony_ci    // Fullfilling both reserved pages requirement and huge code area
1131cb0ef41Sopenharmony_ci    // alignments is not supported (requires re-implementation).
1141cb0ef41Sopenharmony_ci    DCHECK_LE(kMinExpectedOSPageSize, page_allocator->AllocatePageSize());
1151cb0ef41Sopenharmony_ci  }
1161cb0ef41Sopenharmony_ci  DCHECK_IMPLIES(kPlatformRequiresCodeRange,
1171cb0ef41Sopenharmony_ci                 requested <= kMaximalCodeRangeSize);
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  VirtualMemoryCage::ReservationParams params;
1201cb0ef41Sopenharmony_ci  params.page_allocator = page_allocator;
1211cb0ef41Sopenharmony_ci  params.reservation_size = requested;
1221cb0ef41Sopenharmony_ci  // base_alignment should be kAnyBaseAlignment when V8_ENABLE_NEAR_CODE_RANGE
1231cb0ef41Sopenharmony_ci  // is enabled so that InitReservation would not break the alignment in
1241cb0ef41Sopenharmony_ci  // GetAddressHint().
1251cb0ef41Sopenharmony_ci  const size_t allocate_page_size = page_allocator->AllocatePageSize();
1261cb0ef41Sopenharmony_ci  params.base_alignment =
1271cb0ef41Sopenharmony_ci      V8_EXTERNAL_CODE_SPACE_BOOL
1281cb0ef41Sopenharmony_ci          ? base::bits::RoundUpToPowerOfTwo(requested)
1291cb0ef41Sopenharmony_ci          : VirtualMemoryCage::ReservationParams::kAnyBaseAlignment;
1301cb0ef41Sopenharmony_ci  params.base_bias_size = RoundUp(reserved_area, allocate_page_size);
1311cb0ef41Sopenharmony_ci  params.page_size = MemoryChunk::kPageSize;
1321cb0ef41Sopenharmony_ci  params.requested_start_hint =
1331cb0ef41Sopenharmony_ci      GetCodeRangeAddressHint()->GetAddressHint(requested, allocate_page_size);
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  if (!VirtualMemoryCage::InitReservation(params)) return false;
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
1381cb0ef41Sopenharmony_ci    // Ensure that the code range does not cross the 4Gb boundary and thus
1391cb0ef41Sopenharmony_ci    // default compression scheme of truncating the Code pointers to 32-bit
1401cb0ef41Sopenharmony_ci    // still work.
1411cb0ef41Sopenharmony_ci    Address base = page_allocator_->begin();
1421cb0ef41Sopenharmony_ci    Address last = base + page_allocator_->size() - 1;
1431cb0ef41Sopenharmony_ci    CHECK_EQ(GetPtrComprCageBaseAddress(base),
1441cb0ef41Sopenharmony_ci             GetPtrComprCageBaseAddress(last));
1451cb0ef41Sopenharmony_ci  }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci  // On some platforms, specifically Win64, we need to reserve some pages at
1481cb0ef41Sopenharmony_ci  // the beginning of an executable space. See
1491cb0ef41Sopenharmony_ci  //   https://cs.chromium.org/chromium/src/components/crash/content/
1501cb0ef41Sopenharmony_ci  //     app/crashpad_win.cc?rcl=fd680447881449fba2edcf0589320e7253719212&l=204
1511cb0ef41Sopenharmony_ci  // for details.
1521cb0ef41Sopenharmony_ci  if (reserved_area > 0) {
1531cb0ef41Sopenharmony_ci    if (!reservation()->SetPermissions(reservation()->address(), reserved_area,
1541cb0ef41Sopenharmony_ci                                       PageAllocator::kReadWrite)) {
1551cb0ef41Sopenharmony_ci      return false;
1561cb0ef41Sopenharmony_ci    }
1571cb0ef41Sopenharmony_ci  }
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_ci  return true;
1601cb0ef41Sopenharmony_ci}
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_civoid CodeRange::Free() {
1631cb0ef41Sopenharmony_ci  if (IsReserved()) {
1641cb0ef41Sopenharmony_ci    GetCodeRangeAddressHint()->NotifyFreedCodeRange(
1651cb0ef41Sopenharmony_ci        reservation()->region().begin(), reservation()->region().size());
1661cb0ef41Sopenharmony_ci    VirtualMemoryCage::Free();
1671cb0ef41Sopenharmony_ci  }
1681cb0ef41Sopenharmony_ci}
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_ciuint8_t* CodeRange::RemapEmbeddedBuiltins(Isolate* isolate,
1711cb0ef41Sopenharmony_ci                                          const uint8_t* embedded_blob_code,
1721cb0ef41Sopenharmony_ci                                          size_t embedded_blob_code_size) {
1731cb0ef41Sopenharmony_ci  base::MutexGuard guard(&remap_embedded_builtins_mutex_);
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci  // Remap embedded builtins into the end of the address range controlled by
1761cb0ef41Sopenharmony_ci  // the BoundedPageAllocator.
1771cb0ef41Sopenharmony_ci  const base::AddressRegion code_region(page_allocator()->begin(),
1781cb0ef41Sopenharmony_ci                                        page_allocator()->size());
1791cb0ef41Sopenharmony_ci  CHECK_NE(code_region.begin(), kNullAddress);
1801cb0ef41Sopenharmony_ci  CHECK(!code_region.is_empty());
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  uint8_t* embedded_blob_code_copy =
1831cb0ef41Sopenharmony_ci      embedded_blob_code_copy_.load(std::memory_order_acquire);
1841cb0ef41Sopenharmony_ci  if (embedded_blob_code_copy) {
1851cb0ef41Sopenharmony_ci    DCHECK(
1861cb0ef41Sopenharmony_ci        code_region.contains(reinterpret_cast<Address>(embedded_blob_code_copy),
1871cb0ef41Sopenharmony_ci                             embedded_blob_code_size));
1881cb0ef41Sopenharmony_ci    SLOW_DCHECK(memcmp(embedded_blob_code, embedded_blob_code_copy,
1891cb0ef41Sopenharmony_ci                       embedded_blob_code_size) == 0);
1901cb0ef41Sopenharmony_ci    return embedded_blob_code_copy;
1911cb0ef41Sopenharmony_ci  }
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci  const size_t kAllocatePageSize = page_allocator()->AllocatePageSize();
1941cb0ef41Sopenharmony_ci  const size_t kCommitPageSize = page_allocator()->CommitPageSize();
1951cb0ef41Sopenharmony_ci  size_t allocate_code_size =
1961cb0ef41Sopenharmony_ci      RoundUp(embedded_blob_code_size, kAllocatePageSize);
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  // Allocate the re-embedded code blob in the end.
1991cb0ef41Sopenharmony_ci  void* hint = reinterpret_cast<void*>(code_region.end() - allocate_code_size);
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci  embedded_blob_code_copy =
2021cb0ef41Sopenharmony_ci      reinterpret_cast<uint8_t*>(page_allocator()->AllocatePages(
2031cb0ef41Sopenharmony_ci          hint, allocate_code_size, kAllocatePageSize,
2041cb0ef41Sopenharmony_ci          PageAllocator::kNoAccess));
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci  if (!embedded_blob_code_copy) {
2071cb0ef41Sopenharmony_ci    V8::FatalProcessOutOfMemory(
2081cb0ef41Sopenharmony_ci        isolate, "Can't allocate space for re-embedded builtins");
2091cb0ef41Sopenharmony_ci  }
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci  size_t code_size = RoundUp(embedded_blob_code_size, kCommitPageSize);
2121cb0ef41Sopenharmony_ci  if constexpr (base::OS::IsRemapPageSupported()) {
2131cb0ef41Sopenharmony_ci    // By default, the embedded builtins are not remapped, but copied. This
2141cb0ef41Sopenharmony_ci    // costs memory, since builtins become private dirty anonymous memory,
2151cb0ef41Sopenharmony_ci    // rather than shared, clean, file-backed memory for the embedded version.
2161cb0ef41Sopenharmony_ci    // If the OS supports it, we can remap the builtins *on top* of the space
2171cb0ef41Sopenharmony_ci    // allocated in the code range, making the "copy" shared, clean, file-backed
2181cb0ef41Sopenharmony_ci    // memory, and thus saving sizeof(builtins).
2191cb0ef41Sopenharmony_ci    //
2201cb0ef41Sopenharmony_ci    // Builtins should start at a page boundary, see
2211cb0ef41Sopenharmony_ci    // platform-embedded-file-writer-mac.cc. If it's not the case (e.g. if the
2221cb0ef41Sopenharmony_ci    // embedded builtins are not coming from the binary), fall back to copying.
2231cb0ef41Sopenharmony_ci    if (IsAligned(reinterpret_cast<uintptr_t>(embedded_blob_code),
2241cb0ef41Sopenharmony_ci                  kCommitPageSize)) {
2251cb0ef41Sopenharmony_ci      bool ok = base::OS::RemapPages(embedded_blob_code, code_size,
2261cb0ef41Sopenharmony_ci                                     embedded_blob_code_copy,
2271cb0ef41Sopenharmony_ci                                     base::OS::MemoryPermission::kReadExecute);
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci      if (ok) {
2301cb0ef41Sopenharmony_ci        embedded_blob_code_copy_.store(embedded_blob_code_copy,
2311cb0ef41Sopenharmony_ci                                       std::memory_order_release);
2321cb0ef41Sopenharmony_ci        return embedded_blob_code_copy;
2331cb0ef41Sopenharmony_ci      }
2341cb0ef41Sopenharmony_ci    }
2351cb0ef41Sopenharmony_ci  }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci  if (!page_allocator()->SetPermissions(embedded_blob_code_copy, code_size,
2381cb0ef41Sopenharmony_ci                                        PageAllocator::kReadWrite)) {
2391cb0ef41Sopenharmony_ci    V8::FatalProcessOutOfMemory(isolate,
2401cb0ef41Sopenharmony_ci                                "Re-embedded builtins: set permissions");
2411cb0ef41Sopenharmony_ci  }
2421cb0ef41Sopenharmony_ci  memcpy(embedded_blob_code_copy, embedded_blob_code, embedded_blob_code_size);
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  if (!page_allocator()->SetPermissions(embedded_blob_code_copy, code_size,
2451cb0ef41Sopenharmony_ci                                        PageAllocator::kReadExecute)) {
2461cb0ef41Sopenharmony_ci    V8::FatalProcessOutOfMemory(isolate,
2471cb0ef41Sopenharmony_ci                                "Re-embedded builtins: set permissions");
2481cb0ef41Sopenharmony_ci  }
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci  embedded_blob_code_copy_.store(embedded_blob_code_copy,
2511cb0ef41Sopenharmony_ci                                 std::memory_order_release);
2521cb0ef41Sopenharmony_ci  return embedded_blob_code_copy;
2531cb0ef41Sopenharmony_ci}
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci// static
2561cb0ef41Sopenharmony_cistd::shared_ptr<CodeRange> CodeRange::EnsureProcessWideCodeRange(
2571cb0ef41Sopenharmony_ci    v8::PageAllocator* page_allocator, size_t requested_size) {
2581cb0ef41Sopenharmony_ci  base::MutexGuard guard(process_wide_code_range_creation_mutex_.Pointer());
2591cb0ef41Sopenharmony_ci  std::shared_ptr<CodeRange> code_range = process_wide_code_range_.Get().lock();
2601cb0ef41Sopenharmony_ci  if (!code_range) {
2611cb0ef41Sopenharmony_ci    code_range = std::make_shared<CodeRange>();
2621cb0ef41Sopenharmony_ci    if (!code_range->InitReservation(page_allocator, requested_size)) {
2631cb0ef41Sopenharmony_ci      V8::FatalProcessOutOfMemory(
2641cb0ef41Sopenharmony_ci          nullptr, "Failed to reserve virtual memory for CodeRange");
2651cb0ef41Sopenharmony_ci    }
2661cb0ef41Sopenharmony_ci    *process_wide_code_range_.Pointer() = code_range;
2671cb0ef41Sopenharmony_ci  }
2681cb0ef41Sopenharmony_ci  return code_range;
2691cb0ef41Sopenharmony_ci}
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci// static
2721cb0ef41Sopenharmony_cistd::shared_ptr<CodeRange> CodeRange::GetProcessWideCodeRange() {
2731cb0ef41Sopenharmony_ci  return process_wide_code_range_.Get().lock();
2741cb0ef41Sopenharmony_ci}
2751cb0ef41Sopenharmony_ci
2761cb0ef41Sopenharmony_ci}  // namespace internal
2771cb0ef41Sopenharmony_ci}  // namespace v8
278