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