// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "platform/globals.h" #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ defined(DART_HOST_OS_MACOS) #include "bin/virtual_memory.h" #include #include #include #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) #include #endif #include "platform/assert.h" #include "platform/utils.h" namespace dart { namespace bin { // standard MAP_FAILED causes "error: use of old-style cast" as it // defines MAP_FAILED as ((void *) -1) #undef MAP_FAILED #define MAP_FAILED reinterpret_cast(-1) uword VirtualMemory::page_size_ = 0; intptr_t VirtualMemory::CalculatePageSize() { const intptr_t page_size = getpagesize(); ASSERT(page_size != 0); ASSERT(Utils::IsPowerOfTwo(page_size)); return page_size; } VirtualMemory* VirtualMemory::Allocate(intptr_t size, bool is_executable, const char* name) { ASSERT(Utils::IsAligned(size, PageSize())); const int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); int map_flags = MAP_PRIVATE | MAP_ANONYMOUS; #if (defined(DART_HOST_OS_MACOS) && !defined(DART_HOST_OS_IOS)) if (is_executable && IsAtLeastMacOSX10_14()) { map_flags |= MAP_JIT; } #endif // defined(DART_HOST_OS_MACOS) // Some 64-bit microarchitectures store only the low 32-bits of targets as // part of indirect branch prediction, predicting that the target's upper bits // will be same as the call instruction's address. This leads to misprediction // for indirect calls crossing a 4GB boundary. We ask mmap to place our // generated code near the VM binary to avoid this. void* hint = is_executable ? reinterpret_cast(&Allocate) : nullptr; void* address = mmap(hint, size, prot, map_flags, -1, 0); #if defined(DART_HOST_OS_LINUX) // On WSL 1 trying to allocate memory close to the binary by supplying a hint // fails with ENOMEM for unclear reason. Some reports suggest that this might // be related to the alignment of the hint but aligning it by 64Kb does not // make the issue go away in our experiments. Instead just retry without any // hint. if (address == MAP_FAILED && hint != nullptr && Utils::IsWindowsSubsystemForLinux()) { address = mmap(nullptr, size, prot, map_flags, -1, 0); } #endif if (address == MAP_FAILED) { return nullptr; } #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) // PR_SET_VMA was only added to mainline Linux in 5.17, and some versions of // the Android NDK have incorrect headers, so we manually define it if absent. #if !defined(PR_SET_VMA) #define PR_SET_VMA 0x53564d41 #endif #if !defined(PR_SET_VMA_ANON_NAME) #define PR_SET_VMA_ANON_NAME 0 #endif prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, address, size, name); #endif return new VirtualMemory(address, size); } VirtualMemory::~VirtualMemory() { if (address_ != nullptr) { if (munmap(address_, size_) != 0) { int error = errno; const int kBufferSize = 1024; char error_buf[kBufferSize]; FATAL("munmap error: %d (%s)", error, Utils::StrError(error, error_buf, kBufferSize)); } } } void VirtualMemory::Protect(void* address, intptr_t size, Protection mode) { uword start_address = reinterpret_cast(address); uword end_address = start_address + size; uword page_address = Utils::RoundDown(start_address, PageSize()); int prot = 0; switch (mode) { case kNoAccess: prot = PROT_NONE; break; case kReadOnly: prot = PROT_READ; break; case kReadWrite: prot = PROT_READ | PROT_WRITE; break; case kReadExecute: prot = PROT_READ | PROT_EXEC; break; case kReadWriteExecute: prot = PROT_READ | PROT_WRITE | PROT_EXEC; break; } if (mprotect(reinterpret_cast(page_address), end_address - page_address, prot) != 0) { int error = errno; const int kBufferSize = 1024; char error_buf[kBufferSize]; FATAL("mprotect error: %d (%s)", error, Utils::StrError(error, error_buf, kBufferSize)); } } } // namespace bin } // namespace dart #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) || \ // defined(DART_HOST_OS_MACOS)