// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/code-range.h"
#include <algorithm>
#include <atomic>
#include <limits>
#include <utility>
#include "src/base/bits.h"
#include "src/base/lazy-instance.h"
#include "src/base/once.h"
#include "src/codegen/constants-arch.h"
#include "src/common/globals.h"
#include "src/flags/flags.h"
#include "src/heap/heap-inl.h"
#include "src/utils/allocation.h"
#if defined(V8_OS_WIN64)
#include "src/diagnostics/unwinding-info-win64.h"
#endif // V8_OS_WIN64
namespace v8 {
namespace internal {
namespace {
DEFINE_LAZY_LEAKY_OBJECT_GETTER(CodeRangeAddressHint, GetCodeRangeAddressHint)
void FunctionInStaticBinaryForAddressHint() {}
} // anonymous namespace
void RedZones::Initialize(base::BoundedPageAllocator* allocator) {
allocator_ = allocator;
}
bool RedZones::TryAdd(base::AddressRegion region) {
if (!allocator_) {
return false;
}
const base::AddressRegion allocator_region(allocator_->begin(),
allocator_->size());
const base::AddressRegion overlap = allocator_region.GetOverlap(region);
if (overlap.size() == 0) {
return false;
}
// The region passed in here already needs to be sufficiently aligned to the
// allocator.
CHECK_EQ(0, overlap.size() % allocator_->AllocatePageSize());
red_zones_.emplace_back(overlap);
// TODO(429538831): Once better understood, turn this into a regular
// OOM.
CHECK(allocator_->AllocatePagesAt(overlap.begin(), overlap.size(),
PageAllocator::kNoAccess));
return true;
}
// Removes a `needle` from the red zones if it is contained (partially or
// full) in the existing red zones. Returns true if `needle` was removed from
// the existing red zones, and false otherwise.
bool RedZones::TryRemove(base::AddressRegion needle) {
if (!allocator_) {
return false;
}
const size_t allocate_page_size = allocator_->AllocatePageSize();
bool did_remove_region = false;
std::vector<base::AddressRegion> new_red_zones;
for (auto it = red_zones_.begin(); it != red_zones_.end();) {
const base::AddressRegion red_zone_region = *it;
const base::AddressRegion overlap = red_zone_region.GetOverlap(needle);
if (overlap.size() == 0) {
++it;
continue;
}
// We have an overlap. We cannot partially free the red zone here but
// have to return all of it and then conditonally re-add ranges
// manually. This is necessary as we still want the non-removed parts to
// be unavailable for allocation.
CHECK_EQ(0, overlap.size() % allocate_page_size);
allocator_->FreePages(reinterpret_cast<void*>(red_zone_region.begin()),
red_zone_region.size());
it = red_zones_.erase(it);
did_remove_region = true;
// If the overlap was the whole region, we just need to remove the entry and
// we are done.
if (overlap.size() == red_zone_region.size()) {
continue;
}
const auto add_new_red_zone = [this, &new_red_zones, allocate_page_size](
Address red_zone_begin,
size_t red_zone_size) {
CHECK_EQ(0, red_zone_size % allocate_page_size);
CHECK(allocator_->AllocatePagesAt(red_zone_begin, red_zone_size,
PageAllocator::kNoAccess));
new_red_zones.emplace_back(red_zone_begin, red_zone_size);
};
if (red_zone_region.begin() != overlap.begin()) {
CHECK_GT(overlap.begin(), red_zone_region.begin());
add_new_red_zone(red_zone_region.begin(),
overlap.begin() - red_zone_region.begin());
}
if (red_zone_region.end() != overlap.end()) {
CHECK_LT(overlap.end(), red_zone_region.end());
add_new_red_zone(overlap.end(), red_zone_region.end() - overlap.end());
}
}
// Add the new red zones.
red_zones_.insert(red_zones_.end(), new_red_zones.begin(),
new_red_zones.end());
return did_remove_region;
}
Address CodeRangeAddressHint::GetAddressHint(size_t code_range_size,
size_t allocate_page_size) {
base::MutexGuard guard(&mutex_);
Address result = 0;
auto it = recently_freed_.find(code_range_size);
// No recently freed region has been found, try to provide a hint for placing
// a code region.
if (it == recently_freed_.end() || it->second.empty()) {
return RoundUp(FUNCTION_ADDR(&FunctionInStaticBinaryForAddressHint),
allocate_page_size);
}
result = it->second.back();
CHECK(IsAligned(result, allocate_page_size));
it->second.pop_back();
return result;
}
void CodeRangeAddressHint::NotifyFreedCodeRange(Address code_range_start,
size_t code_range_size) {
base::MutexGuard guard(&mutex_);
recently_freed_[code_range_size].push_back(code_range_start);
}
CodeRange::~CodeRange() { Free(); }
// static
size_t CodeRange::GetWritableReservedAreaSize() {
return kReservedCodeRangePages * MemoryAllocator::GetCommitPageSize();
}
#define TRACE(...) \
if (v8_flags.trace_code_range_allocation) PrintF(__VA_ARGS__)
bool CodeRange::InitReservation(v8::PageAllocator* page_allocator,
size_t requested, bool immutable) {
DCHECK_NE(requested, 0);
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
page_allocator = GetPlatformPageAllocator();
}
if (requested <= kMinimumCodeRangeSize) {
requested = kMinimumCodeRangeSize;
}
const size_t kPageSize = NormalPage::kPageSize;
const size_t allocate_page_size = page_allocator->AllocatePageSize();
CHECK(IsAligned(kPageSize, allocate_page_size));
DCHECK_IMPLIES(kPlatformRequiresCodeRange,
requested <= kMaximalCodeRangeSize);
VirtualMemoryCage::ReservationParams params;
params.page_allocator = page_allocator;
params.reservation_size = requested;
params.base_alignment =
VirtualMemoryCage::ReservationParams::kAnyBaseAlignment;
params.page_size = kPageSize;
if (v8_flags.jitless) {
params.permissions = PageAllocator::Permission::kNoAccess;
params.page_initialization_mode =
base::PageInitializationMode::kAllocatedPagesCanBeUninitialized;
params.page_freeing_mode = base::PageFreeingMode::kMakeInaccessible;
} else {
params.permissions = PageAllocator::Permission::kNoAccessWillJitLater;
params.page_initialization_mode =
base::PageInitializationMode::kRecommitOnly;
params.page_freeing_mode = base::PageFreeingMode::kDiscard;
}
#if defined(V8_TARGET_OS_IOS) || defined(V8_TARGET_OS_CHROMEOS)
// iOS:
// We only get one shot at doing MAP_JIT on iOS. So we need to make it
// the least restrictive so it succeeds otherwise we will terminate the
// process on the failed allocation.
// ChromeOS:
// Chrome on ChromeOS uses libgcc unwinding library which seems to work an
// order of magnitude slower if we allocate CodeRange closer to the binary.
// In non-official builds Chrome collects a lot of stack traces just in case,
// so the slowdown of a single backtrace() call results in a noticeable
// increase of test times. As a workaround, do a one shot allocation without
// providing a hint.
// TODO(https://crbug.com/40096218): investigate this ChromeOS issue.
params.requested_start_hint = kNullAddress;
if (!VirtualMemoryCage::InitReservation(params)) return false;
#else
constexpr size_t kRadiusInMB =
kMaxPCRelativeCodeRangeInMB > 1024 ? kMaxPCRelativeCodeRangeInMB : 4096;
auto preferred_region = GetPreferredRegion(kRadiusInMB, kPageSize);
TRACE("=== Preferred region: [%p, %p)\n",
reinterpret_cast<void*>(preferred_region.begin()),
reinterpret_cast<void*>(preferred_region.end()));
// For configurations with enabled pointer compression and shared external
// code range we can afford trying harder to allocate code range near .text
// section.
const bool kShouldTryHarder = V8_EXTERNAL_CODE_SPACE_BOOL &&
COMPRESS_POINTERS_IN_SHARED_CAGE_BOOL &&
v8_flags.better_code_range_allocation;
if (kShouldTryHarder) {
// TODO(v8:11880): consider using base::OS::GetFirstFreeMemoryRangeWithin()
// to avoid attempts that's going to fail anyway.
VirtualMemoryCage candidate_cage;
// Try to allocate code range at the end of preferred region, by going
// towards the start in steps.
const int kAllocationTries = 16;
params.requested_start_hint =
RoundDown(preferred_region.end() - requested, kPageSize);
Address step =
RoundDown(preferred_region.size() / kAllocationTries, kPageSize);
for (int i = 0; i < kAllocationTries; i++) {
TRACE("=== Attempt #%d, hint=%p\n", i,
reinterpret_cast<void*>(params.requested_start_hint));
if (candidate_cage.InitReservation(params)) {
TRACE("=== Attempt #%d (%p): [%p, %p)\n", i,
reinterpret_cast<void*>(params.requested_start_hint),
reinterpret_cast<void*>(candidate_cage.region().begin()),
reinterpret_cast<void*>(candidate_cage.region().end()));
// Allocation succeeded, check if it's in the preferred range.
if (preferred_region.contains(candidate_cage.region())) break;
// This allocation is not the one we are searhing for.
candidate_cage.Free();
}
if (step == 0) break;
params.requested_start_hint -= step;
}
if (candidate_cage.IsReserved()) {
*static_cast<VirtualMemoryCage*>(this) = std::move(candidate_cage);
}
}
if (!IsReserved()) {
Address the_hint = GetCodeRangeAddressHint()->GetAddressHint(
requested, allocate_page_size);
// Last resort, use whatever region we could get with minimum constraints.
params.requested_start_hint = the_hint;
if (!VirtualMemoryCage::InitReservation(params)) {
params.requested_start_hint = kNullAddress;
if (!VirtualMemoryCage::InitReservation(params)) return false;
}
TRACE("=== Fallback attempt, hint=%p: [%p, %p)\n",
reinterpret_cast<void*>(params.requested_start_hint),
reinterpret_cast<void*>(region().begin()),
reinterpret_cast<void*>(region().end()));
}
if (v8_flags.abort_on_far_code_range &&
!preferred_region.contains(region())) {
// We didn't manage to allocate the code range close enough.
FATAL("Failed to allocate code range close to the .text section");
}
#endif // defined(V8_TARGET_OS_IOS) || defined(V8_TARGET_OS_CHROMEOS)
#ifdef V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
// Sandboxed code should never write to code space.
SandboxHardwareSupport::RegisterOutOfSandboxMemory(
base(), size(), PagePermissions::kNoAccess);
#endif // V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
// On some platforms, specifically Win64, we need to reserve some pages at
// the beginning of an executable space. See
// https://cs.chromium.org/chromium/src/components/crash/content/
// app/crashpad_win.cc?rcl=fd680447881449fba2edcf0589320e7253719212&l=204
// for details.
const size_t required_writable_area_size = GetWritableReservedAreaSize();
// The size of the area that might have been excluded from the area
// allocatable by the BoundedPageAllocator.
size_t excluded_allocatable_area_size = 0;
if (required_writable_area_size > 0) {
CHECK_LE(required_writable_area_size, kPageSize);
// If the start of the reservation is not kPageSize-aligned then
// there's a non-allocatable region before the area controlled by
// the BoundedPageAllocator. Use it if it's big enough.
const Address non_allocatable_size = page_allocator_->begin() - base();
TRACE("=== non-allocatable region: [%p, %p)\n",
reinterpret_cast<void*>(base()),
reinterpret_cast<void*>(base() + non_allocatable_size));
// Exclude the first page from allocatable pages if the required writable
// area doesn't fit into the non-allocatable area.
if (non_allocatable_size < required_writable_area_size) {
TRACE("=== Exclude the first page from allocatable area\n");
excluded_allocatable_area_size = kPageSize;
CHECK(page_allocator_->AllocatePagesAt(page_allocator_->begin(),
excluded_allocatable_area_size,
PageAllocator::kNoAccess));
}
// Commit required amount of writable memory.
if (!reservation()->SetPermissions(base(), required_writable_area_size,
PageAllocator::kReadWrite)) {
return false;
}
#if defined(V8_OS_WIN64)
if (win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
win64_unwindinfo::RegisterNonABICompliantCodeRange(
reinterpret_cast<void*>(base()), size());
}
#endif // V8_OS_WIN64
}
#if CONTIGUOUS_COMPRESSED_READ_ONLY_SPACE_BOOL
red_zones_.Initialize(page_allocator_.get());
// Contiguous RO space supports checking for RO space objects via raw
// compressed addresses. In case the code range crosses a
// `kPtrComprCageBaseAlignment` (4GiB) boundary we need to add a
// corresponding red zone to the code range so that no compressed
// code pointers look like RO-space.
//
// Cases:
// - CR: code range
// - RZ: red zone aligned at 4G of size kContiguousReadOnlyReservationSize
// |--------- CR ---------|
// |--- RZ ---|
// |--- RZ ---|
// |--- RZ ---|
// The checks below only work when the code range is not fully contained
// within the aligned region.
CHECK_GE(size(), kContiguousReadOnlyReservationSize);
// Read-only red zone addresses repeat every kPtrComprCageBaseAlignment
// (4GiB), but there's at most one red zone region that overlaps with the
// code range. The code below can deal with multiple overlaps though.
CHECK_GE(kPtrComprCageBaseAlignment - kContiguousReadOnlyReservationSize,
size());
// To keep it simple and avoid computing various cases, we just loop over
// with a window of kPtrComprCageBaseAlignment-aligned possible starts and
// commit those that are contained in the code range.
// We add excluded_allocatable_area_size as that area is generally already
// reserved at the beginning of the code range.
CHECK_EQ(0, excluded_allocatable_area_size % allocate_page_size);
CHECK_EQ(0, kContiguousReadOnlyReservationSize % allocate_page_size);
const Address allocatable_begin =
page_allocator_->begin() + excluded_allocatable_area_size;
const Address allocatable_end =
page_allocator_->begin() + page_allocator_->size();
const Address window_begin =
RoundDown<kPtrComprCageBaseAlignment>(allocatable_begin);
const Address window_end =
RoundUp<kPtrComprCageBaseAlignment>(allocatable_end);
int number_of_red_zones = 0;
for (Address current = window_begin; current <= window_end;
current += kPtrComprCageBaseAlignment) {
const Address red_zone_start = std::max(current, allocatable_begin);
const Address red_zone_end =
std::min(current + kContiguousReadOnlyReservationSize, allocatable_end);
if (red_zone_start < red_zone_end) {
number_of_red_zones++;
const size_t red_zone_size = red_zone_end - red_zone_start;
// Sanity check until bug is resolved.
CHECK_LT(red_zone_size, size());
CHECK_GE(red_zone_start, base());
CHECK_LE(red_zone_end, base() + size());
CHECK(red_zones_.TryAdd(
base::AddressRegion(red_zone_start, red_zone_size)));
}
}
CHECK_LE(red_zones_.num_red_zones(), 1);
CHECK_LE(number_of_red_zones, 1);
#endif // CONTIGUOUS_COMPRESSED_READ_ONLY_SPACE_BOOL
// Don't pre-commit the code cage on Windows since it uses memory and it's not
// required for recommit.
// iOS cannot adjust page permissions for MAP_JIT'd pages, they are set as RWX
// at the start.
#if !defined(V8_OS_WIN) && !defined(V8_OS_IOS)
if (params.page_initialization_mode ==
base::PageInitializationMode::kRecommitOnly) {
void* base = reinterpret_cast<void*>(page_allocator_->begin() +
excluded_allocatable_area_size);
size_t size = page_allocator_->size() - excluded_allocatable_area_size;
if (ThreadIsolation::Enabled()) {
if (!ThreadIsolation::MakeExecutable(reinterpret_cast<Address>(base),
size)) {
return false;
}
} else if (!params.page_allocator->SetPermissions(
base, size, PageAllocator::kReadWriteExecute)) {
return false;
}
if (immutable) {
#ifdef DEBUG
immutable_ = true;
#endif
#ifdef V8_ENABLE_MEMORY_SEALING
params.page_allocator->SealPages(base, size);
#endif
}
DiscardSealedMemoryScope discard_scope("Discard global code range.");
if (!params.page_allocator->DiscardSystemPages(base, size)) return false;
}
#endif // !defined(V8_OS_WIN)
return true;
}
// Preferred region for the code range is an intersection of the following
// regions:
// a) [builtins - kMaxPCRelativeDistance, builtins + kMaxPCRelativeDistance)
// b) [RoundDown(builtins, 4GB), RoundUp(builtins, 4GB)) in order to ensure
// Requirement (a) is there to avoid remaping of embedded builtins into
// the code for architectures where PC-relative jump/call distance is big
// enough.
// Requirement (b) is aiming at helping CPU branch predictors in general and
// in case V8_EXTERNAL_CODE_SPACE is enabled it ensures that
// ExternalCodeCompressionScheme works for all pointers in the code range.
// static
base::AddressRegion CodeRange::GetPreferredRegion(size_t radius_in_megabytes,
size_t allocate_page_size) {
#ifdef V8_TARGET_ARCH_64_BIT
// Compute builtins location.
Address embedded_blob_code_start =
reinterpret_cast<Address>(Isolate::CurrentEmbeddedBlobCode());
Address embedded_blob_code_end;
if (embedded_blob_code_start == kNullAddress) {
// When there's no embedded blob use address of a function from the binary
// as an approximation.
embedded_blob_code_start =
FUNCTION_ADDR(&FunctionInStaticBinaryForAddressHint);
embedded_blob_code_end = embedded_blob_code_start + 1;
} else {
embedded_blob_code_end =
embedded_blob_code_start + Isolate::CurrentEmbeddedBlobCodeSize();
}
// Fulfil requirement (a).
constexpr size_t max_size = std::numeric_limits<size_t>::max();
size_t radius = radius_in_megabytes * MB;
Address region_start =
RoundUp(embedded_blob_code_end - radius, allocate_page_size);
if (region_start > embedded_blob_code_end) {
// |region_start| underflowed.
region_start = 0;
}
Address region_end =
RoundDown(embedded_blob_code_start + radius, allocate_page_size);
if (region_end < embedded_blob_code_start) {
// |region_end| overflowed.
region_end = RoundDown(max_size, allocate_page_size);
}
// Fulfil requirement (b).
constexpr size_t k4GB = size_t{4} * GB;
Address four_gb_cage_start = RoundDown(embedded_blob_code_start, k4GB);
Address four_gb_cage_end = four_gb_cage_start + k4GB;
region_start = std::max(region_start, four_gb_cage_start);
region_end = std::min(region_end, four_gb_cage_end);
return base::AddressRegion(region_start, region_end - region_start);
#else
return {};
#endif // V8_TARGET_ARCH_64_BIT
}
void CodeRange::Free() {
// TODO(361480580): this DCHECK is temporarily disabled since we free the
// global CodeRange in the PoolTest.
// DCHECK(!immutable_);
if (IsReserved()) {
#if defined(V8_OS_WIN64)
if (win64_unwindinfo::CanRegisterUnwindInfoForNonABICompliantCodeRange()) {
win64_unwindinfo::UnregisterNonABICompliantCodeRange(
reinterpret_cast<void*>(base()));
}
#endif // V8_OS_WIN64
GetCodeRangeAddressHint()->NotifyFreedCodeRange(
reservation()->region().begin(), reservation()->region().size());
VirtualMemoryCage::Free();
}
}
uint8_t* CodeRange::RemapEmbeddedBuiltins(Isolate* isolate,
const uint8_t* embedded_blob_code,
size_t embedded_blob_code_size) {
base::MutexGuard guard(&remap_embedded_builtins_mutex_);
// Remap embedded builtins into the end of the address range controlled by
// the BoundedPageAllocator.
const base::AddressRegion code_region(page_allocator()->begin(),
page_allocator()->size());
CHECK_NE(code_region.begin(), kNullAddress);
CHECK(!code_region.is_empty());
uint8_t* embedded_blob_code_copy =
embedded_blob_code_copy_.load(std::memory_order_acquire);
if (embedded_blob_code_copy) {
DCHECK(
code_region.contains(reinterpret_cast<Address>(embedded_blob_code_copy),
embedded_blob_code_size));
SLOW_DCHECK(memcmp(embedded_blob_code, embedded_blob_code_copy,
embedded_blob_code_size) == 0);
return embedded_blob_code_copy;
}
const size_t allocate_page_size = page_allocator()->AllocatePageSize();
const size_t allocate_code_size =
RoundUp(embedded_blob_code_size, allocate_page_size);
// Allocate the re-embedded code blob in such a way that it will be reachable
// by PC-relative addressing from biggest possible region. Start near the
// end and step downward if the target is already occupied (e.g. by code
// compiled before the blob was created, or by a red zone).
const size_t max_pc_relative_code_range = kMaxPCRelativeCodeRangeInMB * MB;
size_t blob_offset =
std::min(max_pc_relative_code_range, code_region.size()) -
allocate_code_size;
Address blob_begin;
while (true) {
blob_begin = code_region.begin() + blob_offset;
// TODO(429538831): This should be using AllocatePagesAt() which lacks
// support for kRecommitOnly though.
embedded_blob_code_copy =
reinterpret_cast<uint8_t*>(page_allocator()->AllocatePages(
reinterpret_cast<void*>(blob_begin), allocate_code_size,
allocate_page_size, PageAllocator::kNoAccessWillJitLater));
if (embedded_blob_code_copy == reinterpret_cast<uint8_t*>(blob_begin)) {
break;
}
// The allocation didn't land at the requested address. Free the
// wrongly-placed allocation and try a lower address.
if (embedded_blob_code_copy) {
page_allocator()->FreePages(embedded_blob_code_copy, allocate_code_size);
embedded_blob_code_copy = nullptr;
}
if (blob_offset < allocate_code_size) {
V8::FatalProcessOutOfMemory(
isolate, "Can't allocate space for re-embedded builtins");
}
blob_offset -= allocate_code_size;
}
if (code_region.size() > max_pc_relative_code_range) {
// The re-embedded code blob might not be reachable from the end part of
// the code range, so ensure that code pages will never be allocated in
// the "unreachable" area.
const Address unreachable_start =
reinterpret_cast<Address>(embedded_blob_code_copy) +
max_pc_relative_code_range;
if (code_region.contains(unreachable_start)) {
const size_t unreachable_size = code_region.end() - unreachable_start;
red_zones_.TryRemove(
base::AddressRegion(unreachable_start, unreachable_size));
// TODO(429538831): This should be using AllocatePagesAt() which lacks
// support for kRecommitOnly though.
void* result = page_allocator()->AllocatePages(
reinterpret_cast<void*>(unreachable_start), unreachable_size,
allocate_page_size, PageAllocator::kNoAccess);
CHECK_EQ(reinterpret_cast<Address>(result), unreachable_start);
}
}
// Clear the red zones to make them unusable.
red_zones_.Reset();
const size_t commit_page_size = page_allocator()->CommitPageSize();
size_t code_size = RoundUp(embedded_blob_code_size, commit_page_size);
if constexpr (base::OS::IsRemapPageSupported()) {
// By default, the embedded builtins are not remapped, but copied. This
// costs memory, since builtins become private dirty anonymous memory,
// rather than shared, clean, file-backed memory for the embedded version.
// If the OS supports it, we can remap the builtins *on top* of the space
// allocated in the code range, making the "copy" shared, clean, file-backed
// memory, and thus saving sizeof(builtins).
//
// Builtins should start at a page boundary, see
// platform-embedded-file-writer-mac.cc. If it's not the case (e.g. if the
// embedded builtins are not coming from the binary), fall back to copying.
bool source_is_remappable = IsAligned(
reinterpret_cast<uintptr_t>(embedded_blob_code), commit_page_size);
#if defined(V8_OS_LINUX)
// On Linux, RemapPages is destructive unless the source is a
// file-backed mapping, so only remap when the embedded blob is the one
// embedded in the binary (and hence file-backed).
source_is_remappable &= Isolate::CurrentEmbeddedBlobIsBinaryEmbedded();
#endif
if (source_is_remappable) {
bool ok = base::OS::RemapPages(embedded_blob_code, code_size,
embedded_blob_code_copy,
base::OS::MemoryPermission::kReadExecute);
if (ok) {
embedded_blob_code_copy_.store(embedded_blob_code_copy,
std::memory_order_release);
return embedded_blob_code_copy;
}
}
}
if (V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT ||
V8_HEAP_USE_BECORE_JIT_WRITE_PROTECT || ThreadIsolation::Enabled()) {
// iOS code pages are already RWX and don't need to be modified.
#if !defined(V8_TARGET_OS_IOS)
if (!page_allocator()->RecommitPages(embedded_blob_code_copy, code_size,
PageAllocator::kReadWriteExecute)) {
V8::FatalProcessOutOfMemory(isolate,
"Re-embedded builtins: recommit pages");
}
#endif // defined(V8_TARGET_OS_IOS)
RwxMemoryWriteScope rwx_write_scope(
"Enable write access to copy the blob code into the code range");
memcpy(embedded_blob_code_copy, embedded_blob_code,
embedded_blob_code_size);
} else {
if (!page_allocator()->SetPermissions(embedded_blob_code_copy, code_size,
PageAllocator::kReadWrite)) {
V8::FatalProcessOutOfMemory(isolate,
"Re-embedded builtins: set permissions");
}
memcpy(embedded_blob_code_copy, embedded_blob_code,
embedded_blob_code_size);
if (!page_allocator()->SetPermissions(embedded_blob_code_copy, code_size,
PageAllocator::kReadExecute)) {
V8::FatalProcessOutOfMemory(isolate,
"Re-embedded builtins: set permissions");
}
}
embedded_blob_code_copy_.store(embedded_blob_code_copy,
std::memory_order_release);
return embedded_blob_code_copy;
}
#undef TRACE
} // namespace internal
} // namespace v8