diff options
author | 2018-08-23 16:36:36 +0100 | |
---|---|---|
committer | 2018-08-31 11:50:53 +0100 | |
commit | c09cd0513b860c4301ca7bd6a9065b583d102295 (patch) | |
tree | 1c7aa4ed2b9b5b1335829461e764c94565afb71d | |
parent | 40f8a74cba008af768316f0b5bfe9502174b6d02 (diff) |
Reserve boot image memory in one go.
Load boot image components into the reserved memory.
Test: m test-art-host-gtest
Test: testrunner.py --host
Test: Pixel 2 XL boots.
Test: m test-art-target-gtest
Test: testrunner.py --target --optimizing
Bug: 77856493
Change-Id: I214f947979bc0bbfc6df4312527504e90b88a01d
-rw-r--r-- | dex2oat/dex2oat_test.cc | 96 | ||||
-rw-r--r-- | dex2oat/linker/elf_writer_test.cc | 45 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer_test.cc | 48 | ||||
-rw-r--r-- | dexlayout/dexdiag_test.cc | 10 | ||||
-rw-r--r-- | libartbase/base/mem_map.cc | 102 | ||||
-rw-r--r-- | libartbase/base/mem_map.h | 58 | ||||
-rw-r--r-- | libartbase/base/mem_map_test.cc | 106 | ||||
-rw-r--r-- | libartbase/base/zip_archive.cc | 18 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 42 | ||||
-rw-r--r-- | runtime/dexopt_test.cc | 8 | ||||
-rw-r--r-- | runtime/elf_file.cc | 127 | ||||
-rw-r--r-- | runtime/elf_file.h | 14 | ||||
-rw-r--r-- | runtime/elf_file_impl.h | 20 | ||||
-rw-r--r-- | runtime/gc/collector/immune_spaces_test.cc | 6 | ||||
-rw-r--r-- | runtime/gc/space/image_space.cc | 318 | ||||
-rw-r--r-- | runtime/gc/space/image_space.h | 2 | ||||
-rw-r--r-- | runtime/gc/space/image_space_test.cc | 10 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.cc | 1 | ||||
-rw-r--r-- | runtime/oat_file.cc | 230 | ||||
-rw-r--r-- | runtime/oat_file.h | 8 | ||||
-rw-r--r-- | runtime/oat_file_assistant.cc | 12 | ||||
-rw-r--r-- | runtime/oat_file_test.cc | 28 | ||||
-rw-r--r-- | runtime/vdex_file.cc | 5 | ||||
-rw-r--r-- | test/305-other-fault-handler/fault_handler.cc | 1 |
24 files changed, 897 insertions, 418 deletions
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index b2fe441582..fb19a277e9 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -139,11 +139,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; @@ -159,11 +159,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() == nullptr); } @@ -516,11 +516,11 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; EXPECT_GT(app_image_file.length(), 0u); @@ -787,11 +787,11 @@ class Dex2oatLayoutTest : public Dex2oatTest { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; @@ -949,11 +949,11 @@ class Dex2oatUnquickenTest : public Dex2oatTest { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; ASSERT_GE(odex_file->GetOatDexFiles().size(), 1u); @@ -1329,11 +1329,11 @@ TEST_F(Dex2oatTest, LayoutSections) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_filename.c_str(), oat_filename.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex->GetLocation().c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); @@ -1439,11 +1439,11 @@ TEST_F(Dex2oatTest, GenerateCompactDex) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_filename.c_str(), oat_filename.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); @@ -1684,11 +1684,11 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_filename.c_str(), oat_filename.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, temp_dex.GetFilename().c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); @@ -1762,11 +1762,11 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); ASSERT_STREQ("install", odex_file->GetCompilationReason()); @@ -1788,11 +1788,11 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); ASSERT_EQ(nullptr, odex_file->GetCompilationReason()); @@ -1826,11 +1826,11 @@ TEST_F(Dex2oatTest, DontExtract) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/ false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr) << dex_location; std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); @@ -2065,11 +2065,11 @@ TEST_F(Dex2oatTest, AppImageNoProfile) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, odex_location.c_str(), odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, odex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); ImageHeader header = {}; diff --git a/dex2oat/linker/elf_writer_test.cc b/dex2oat/linker/elf_writer_test.cc index b2be003b5d..40495f33ee 100644 --- a/dex2oat/linker/elf_writer_test.cc +++ b/dex2oat/linker/elf_writer_test.cc @@ -14,9 +14,12 @@ * limitations under the License. */ +#include <sys/mman.h> // For the PROT_NONE constant. + #include "elf_file.h" #include "base/file_utils.h" +#include "base/mem_map.h" #include "base/unix_file/fd_file.h" #include "base/utils.h" #include "common_compiler_test.h" @@ -65,8 +68,8 @@ TEST_F(ElfWriterTest, dlsym) { { std::string error_msg; std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, + /* writable */ false, + /* program_header_only */ false, /*low_4gb*/false, &error_msg)); CHECK(ef.get() != nullptr) << error_msg; @@ -77,9 +80,9 @@ TEST_F(ElfWriterTest, dlsym) { { std::string error_msg; std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, - /*low_4gb*/false, + /* writable */ false, + /* program_header_only */ false, + /* low_4gb */ false, &error_msg)); CHECK(ef.get() != nullptr) << error_msg; EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true); @@ -87,16 +90,28 @@ TEST_F(ElfWriterTest, dlsym) { EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true); } { - uint8_t* base = reinterpret_cast<uint8_t*>(ART_BASE_ADDRESS); std::string error_msg; std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - true, - /*low_4gb*/false, - &error_msg, - base)); + /* writable */ false, + /* program_header_only */ true, + /* low_4gb */ false, + &error_msg)); CHECK(ef.get() != nullptr) << error_msg; - CHECK(ef->Load(file.get(), false, /*low_4gb*/false, &error_msg)) << error_msg; + size_t size; + bool success = ef->GetLoadedSize(&size, &error_msg); + CHECK(success) << error_msg; + MemMap reservation = MemMap::MapAnonymous("ElfWriterTest#dlsym reservation", + /* addr */ nullptr, + RoundUp(size, kPageSize), + PROT_NONE, + /* low_4gb */ true, + &error_msg); + CHECK(reservation.IsValid()) << error_msg; + uint8_t* base = reservation.Begin(); + success = + ef->Load(file.get(), /* executable */ false, /* low_4gb */ false, &reservation, &error_msg); + CHECK(success) << error_msg; + CHECK(!reservation.IsValid()); EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatdata) + reinterpret_cast<uintptr_t>(base), reinterpret_cast<uintptr_t>(ef->FindDynamicSymbolAddress("oatdata"))); EXPECT_EQ(reinterpret_cast<uintptr_t>(dl_oatexec) + reinterpret_cast<uintptr_t>(base), @@ -116,9 +131,9 @@ TEST_F(ElfWriterTest, CheckBuildIdPresent) { { std::string error_msg; std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), - false, - false, - /*low_4gb*/false, + /* writable */ false, + /* program_header_only */ false, + /* low_4gb */ false, &error_msg)); CHECK(ef.get() != nullptr) << error_msg; EXPECT_TRUE(ef->HasSection(".note.gnu.build-id")); diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 0264b09eda..bd09f239ba 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -406,11 +406,11 @@ TEST_F(OatTest, WriteRead) { std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/true, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ true, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(oat_file.get() != nullptr) << error_msg; const OatHeader& oat_header = oat_file->GetOatHeader(); @@ -529,11 +529,11 @@ TEST_F(OatTest, EmptyTextSection) { std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(oat_file != nullptr); EXPECT_LT(static_cast<size_t>(oat_file->Size()), @@ -607,11 +607,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), - nullptr, - nullptr, - false, + /* requested_base */ nullptr, + /* executable */ false, low_4gb, - nullptr, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; if (low_4gb) { @@ -738,11 +738,11 @@ void OatTest::TestZipFileInput(bool verify) { std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); @@ -788,11 +788,11 @@ void OatTest::TestZipFileInput(bool verify) { std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(/* zip_fd */ -1, tmp_oat.GetFilename(), tmp_oat.GetFilename(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(opened_oat_file != nullptr) << error_msg; ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); diff --git a/dexlayout/dexdiag_test.cc b/dexlayout/dexdiag_test.cc index 53145c22fa..60dd7e42a4 100644 --- a/dexlayout/dexdiag_test.cc +++ b/dexlayout/dexdiag_test.cc @@ -71,11 +71,11 @@ class DexDiagTest : public CommonRuntimeTest { std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); EXPECT_TRUE(oat != nullptr) << error_msg; return oat; diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc index a82da2d8c8..1bf553d293 100644 --- a/libartbase/base/mem_map.cc +++ b/libartbase/base/mem_map.cc @@ -227,6 +227,33 @@ bool MemMap::CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byt return false; } +bool MemMap::CheckReservation(uint8_t* expected_ptr, + size_t byte_count, + const char* name, + const MemMap& reservation, + /*out*/std::string* error_msg) { + if (!reservation.IsValid()) { + *error_msg = StringPrintf("Invalid reservation for %s", name); + return false; + } + DCHECK_ALIGNED(reservation.Begin(), kPageSize); + if (reservation.Begin() != expected_ptr) { + *error_msg = StringPrintf("Bad image reservation start for %s: %p instead of %p", + name, + reservation.Begin(), + expected_ptr); + return false; + } + if (byte_count > reservation.Size()) { + *error_msg = StringPrintf("Insufficient reservation, required %zu, available %zu", + byte_count, + reservation.Size()); + return false; + } + return true; +} + + #if USE_ART_LOW_4G_ALLOCATOR void* MemMap::TryMemMapLow4GB(void* ptr, size_t page_aligned_byte_count, @@ -280,7 +307,8 @@ MemMap MemMap::MapAnonymous(const char* name, int prot, bool low_4gb, bool reuse, - std::string* error_msg, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg, bool use_debug_name) { #ifndef __LP64__ UNUSED(low_4gb); @@ -296,9 +324,16 @@ MemMap MemMap::MapAnonymous(const char* name, // reuse means it is okay that it overlaps an existing page mapping. // Only use this if you actually made the page reservation yourself. CHECK(addr != nullptr); + DCHECK(reservation == nullptr); DCHECK(ContainedWithinExistingMap(addr, byte_count, error_msg)) << *error_msg; flags |= MAP_FIXED; + } else if (reservation != nullptr) { + CHECK(addr != nullptr); + if (!CheckReservation(addr, byte_count, name, *reservation, error_msg)) { + return MemMap::Invalid(); + } + flags |= MAP_FIXED; } unique_fd fd; @@ -340,6 +375,11 @@ MemMap MemMap::MapAnonymous(const char* name, SetDebugName(actual, name, page_aligned_byte_count); } + if (reservation != nullptr) { + // Re-mapping was successful, transfer the ownership of the memory to the new MemMap. + DCHECK_EQ(actual, reservation->Begin()); + reservation->ReleaseReservedMemory(byte_count); + } return MemMap(name, reinterpret_cast<uint8_t*>(actual), byte_count, @@ -445,22 +485,30 @@ MemMap MemMap::MapFileAtAddress(uint8_t* expected_ptr, int fd, off_t start, bool low_4gb, - bool reuse, const char* filename, - std::string* error_msg) { + bool reuse, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { CHECK_NE(0, prot); CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE)); - // Note that we do not allow MAP_FIXED unless reuse == true, i.e we - // expect his mapping to be contained within an existing map. + // Note that we do not allow MAP_FIXED unless reuse == true or we have an existing + // reservation, i.e we expect this mapping to be contained within an existing map. if (reuse) { // reuse means it is okay that it overlaps an existing page mapping. // Only use this if you actually made the page reservation yourself. CHECK(expected_ptr != nullptr); + DCHECK(reservation == nullptr); DCHECK(error_msg != nullptr); DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << ((error_msg != nullptr) ? *error_msg : std::string()); flags |= MAP_FIXED; + } else if (reservation != nullptr) { + DCHECK(error_msg != nullptr); + if (!CheckReservation(expected_ptr, byte_count, filename, *reservation, error_msg)) { + return Invalid(); + } + flags |= MAP_FIXED; } else { CHECK_EQ(0, flags & MAP_FIXED); // Don't bother checking for an overlapping region here. We'll @@ -522,6 +570,11 @@ MemMap MemMap::MapFileAtAddress(uint8_t* expected_ptr, page_aligned_byte_count -= redzone_size; } + if (reservation != nullptr) { + // Re-mapping was successful, transfer the ownership of the memory to the new MemMap. + DCHECK_EQ(actual, reservation->Begin()); + reservation->ReleaseReservedMemory(byte_count); + } return MemMap(filename, actual + page_offset, byte_count, @@ -699,6 +752,45 @@ MemMap MemMap::RemapAtEnd(uint8_t* new_end, return MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false); } +MemMap MemMap::TakeReservedMemory(size_t byte_count) { + uint8_t* begin = Begin(); + ReleaseReservedMemory(byte_count); // Performs necessary DCHECK()s on this reservation. + size_t base_size = RoundUp(byte_count, kPageSize); + return MemMap(name_, begin, byte_count, begin, base_size, prot_, /* reuse */ false); +} + +void MemMap::ReleaseReservedMemory(size_t byte_count) { + // Check the reservation mapping. + DCHECK(IsValid()); + DCHECK(!reuse_); + DCHECK(!already_unmapped_); + DCHECK_EQ(redzone_size_, 0u); + DCHECK_EQ(begin_, base_begin_); + DCHECK_EQ(size_, base_size_); + DCHECK_ALIGNED(begin_, kPageSize); + DCHECK_ALIGNED(size_, kPageSize); + + // Check and round up the `byte_count`. + DCHECK_NE(byte_count, 0u); + DCHECK_LE(byte_count, size_); + byte_count = RoundUp(byte_count, kPageSize); + + if (byte_count == size_) { + Invalidate(); + } else { + // Shrink the reservation MemMap and update its `gMaps` entry. + std::lock_guard<std::mutex> mu(*mem_maps_lock_); + auto it = GetGMapsEntry(*this); + // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert. + gMaps->erase(it); + begin_ += byte_count; + size_ -= byte_count; + base_begin_ = begin_; + base_size_ = size_; + gMaps->emplace(base_begin_, this); + } +} + void MemMap::MadviseDontNeedAndZero() { if (base_begin_ != nullptr || base_size_ != 0) { if (!kMadviseZeroes) { diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h index f5fead0ed2..20eda324e1 100644 --- a/libartbase/base/mem_map.h +++ b/libartbase/base/mem_map.h @@ -119,7 +119,10 @@ class MemMap { // Request an anonymous region of length 'byte_count' and a requested base address. // Use null as the requested base address if you don't care. - // "reuse" allows re-mapping an address range from an existing mapping. + // + // `reuse` allows re-mapping an address range from an existing mapping which retains the + // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an + // existing reservation mapping, transferring the ownership of the memory to the new MemMap. // // The word "anonymous" in this context means "not backed by a file". The supplied // 'name' will be used -- on systems that support it -- to give the mapping @@ -132,15 +135,23 @@ class MemMap { int prot, bool low_4gb, bool reuse, - std::string* error_msg, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg, bool use_debug_name = true); static MemMap MapAnonymous(const char* name, uint8_t* addr, size_t byte_count, int prot, bool low_4gb, - std::string* error_msg) { - return MapAnonymous(name, addr, byte_count, prot, low_4gb, /* reuse */ false, error_msg); + /*out*/std::string* error_msg) { + return MapAnonymous(name, + addr, + byte_count, + prot, + low_4gb, + /* reuse */ false, + /* reservation */ nullptr, + error_msg); } // Create placeholder for a region allocated by direct call to mmap. @@ -167,18 +178,23 @@ class MemMap { flags, fd, start, - /*low_4gb*/low_4gb, - /*reuse*/false, + /* low_4gb */ low_4gb, filename, + /* reuse */ false, + /* reservation */ nullptr, error_msg); } // Map part of a file, taking care of non-page aligned offsets. The "start" offset is absolute, // not relative. This version allows requesting a specific address for the base of the mapping. - // "reuse" allows us to create a view into an existing mapping where we do not take ownership of - // the memory. If error_msg is null then we do not print /proc/maps to the log if - // MapFileAtAddress fails. This helps improve performance of the fail case since reading and - // printing /proc/maps takes several milliseconds in the worst case. + // + // `reuse` allows re-mapping an address range from an existing mapping which retains the + // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an + // existing reservation mapping, transferring the ownership of the memory to the new MemMap. + // + // If error_msg is null then we do not print /proc/maps to the log if MapFileAtAddress fails. + // This helps improve performance of the fail case since reading and printing /proc/maps takes + // several milliseconds in the worst case. // // On success, returns returns a valid MemMap. On failure, returns an invalid MemMap. static MemMap MapFileAtAddress(uint8_t* addr, @@ -188,9 +204,10 @@ class MemMap { int fd, off_t start, bool low_4gb, - bool reuse, const char* filename, - std::string* error_msg); + bool reuse, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg); const std::string& GetName() const { return name_; @@ -244,6 +261,14 @@ class MemMap { std::string* error_msg, bool use_debug_name = true); + // Take ownership of pages at the beginning of the mapping. The mapping must be an + // anonymous reservation mapping, owning entire pages. The `byte_count` must not + // exceed the size of this reservation. + // + // Returns a mapping owning `byte_count` bytes rounded up to entire pages + // with size set to the passed `byte_count`. + MemMap TakeReservedMemory(size_t byte_count); + static bool CheckNoGaps(MemMap& begin_map, MemMap& end_map) REQUIRES(!MemMap::mem_maps_lock_); static void DumpMaps(std::ostream& os, bool terse = false) @@ -307,12 +332,21 @@ class MemMap { off_t offset) REQUIRES(!MemMap::mem_maps_lock_); + // Release memory owned by a reservation mapping. + void ReleaseReservedMemory(size_t byte_count); + // member function to access real_munmap static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte_count, std::string* error_msg); + static bool CheckReservation(uint8_t* expected_ptr, + size_t byte_count, + const char* name, + const MemMap& reservation, + /*out*/std::string* error_msg); + std::string name_; uint8_t* begin_ = nullptr; // Start of data. May be changed by AlignBy. size_t size_ = 0u; // Length of data. diff --git a/libartbase/base/mem_map_test.cc b/libartbase/base/mem_map_test.cc index 396f12b421..ab3d18ff04 100644 --- a/libartbase/base/mem_map_test.cc +++ b/libartbase/base/mem_map_test.cc @@ -540,6 +540,7 @@ TEST_F(MemMapTest, MapAnonymousReuse) { PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, + /* reservation */ nullptr, &error_msg); ASSERT_TRUE(map.IsValid()); ASSERT_TRUE(error_msg.empty()); @@ -549,6 +550,7 @@ TEST_F(MemMapTest, MapAnonymousReuse) { PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ true, + /* reservation */ nullptr, &error_msg); ASSERT_TRUE(map2.IsValid()); ASSERT_TRUE(error_msg.empty()); @@ -720,4 +722,108 @@ TEST_F(MemMapTest, AlignBy) { } } +TEST_F(MemMapTest, Reservation) { + CommonInit(); + std::string error_msg; + ScratchFile scratch_file; + constexpr size_t kMapSize = 5 * kPageSize; + std::unique_ptr<uint8_t[]> data(new uint8_t[kMapSize]()); + ASSERT_TRUE(scratch_file.GetFile()->WriteFully(&data[0], kMapSize)); + + MemMap reservation = MemMap::MapAnonymous("Test reservation", + /* addr */ nullptr, + kMapSize, + PROT_NONE, + /* low_4gb */ false, + &error_msg); + ASSERT_TRUE(reservation.IsValid()); + ASSERT_TRUE(error_msg.empty()); + + // Map first part of the reservation. + constexpr size_t kChunk1Size = kPageSize - 1u; + static_assert(kChunk1Size < kMapSize, "We want to split the reservation."); + uint8_t* addr1 = reservation.Begin(); + MemMap map1 = MemMap::MapFileAtAddress(addr1, + /* byte_count */ kChunk1Size, + PROT_READ, + MAP_PRIVATE, + scratch_file.GetFd(), + /* start */ 0, + /* low_4gb */ false, + scratch_file.GetFilename().c_str(), + /* reuse */ false, + &reservation, + &error_msg); + ASSERT_TRUE(map1.IsValid()) << error_msg; + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(map1.Size(), kChunk1Size); + ASSERT_EQ(addr1, map1.Begin()); + ASSERT_TRUE(reservation.IsValid()); + // Entire pages are taken from the `reservation`. + ASSERT_LT(map1.End(), map1.BaseEnd()); + ASSERT_EQ(map1.BaseEnd(), reservation.Begin()); + + // Map second part as an anonymous mapping. + constexpr size_t kChunk2Size = 2 * kPageSize; + DCHECK_LT(kChunk2Size, reservation.Size()); // We want to split the reservation. + uint8_t* addr2 = reservation.Begin(); + MemMap map2 = MemMap::MapAnonymous("MiddleReservation", + addr2, + /* byte_count */ kChunk2Size, + PROT_READ, + /* low_4gb */ false, + /* reuse */ false, + &reservation, + &error_msg); + ASSERT_TRUE(map2.IsValid()) << error_msg; + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(map2.Size(), kChunk2Size); + ASSERT_EQ(addr2, map2.Begin()); + ASSERT_EQ(map2.End(), map2.BaseEnd()); // kChunk2Size is page aligned. + ASSERT_EQ(map2.BaseEnd(), reservation.Begin()); + + // Map the rest of the reservation except the last byte. + const size_t kChunk3Size = reservation.Size() - 1u; + uint8_t* addr3 = reservation.Begin(); + MemMap map3 = MemMap::MapFileAtAddress(addr3, + /* byte_count */ kChunk3Size, + PROT_READ, + MAP_PRIVATE, + scratch_file.GetFd(), + /* start */ dchecked_integral_cast<size_t>(addr3 - addr1), + /* low_4gb */ false, + scratch_file.GetFilename().c_str(), + /* reuse */ false, + &reservation, + &error_msg); + ASSERT_TRUE(map3.IsValid()) << error_msg; + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(map3.Size(), kChunk3Size); + ASSERT_EQ(addr3, map3.Begin()); + // Entire pages are taken from the `reservation`, so it's now exhausted. + ASSERT_FALSE(reservation.IsValid()); + + // Now split the MiddleReservation. + constexpr size_t kChunk2ASize = kPageSize - 1u; + DCHECK_LT(kChunk2ASize, map2.Size()); // We want to split the reservation. + MemMap map2a = map2.TakeReservedMemory(kChunk2ASize); + ASSERT_TRUE(map2a.IsValid()) << error_msg; + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(map2a.Size(), kChunk2ASize); + ASSERT_EQ(addr2, map2a.Begin()); + ASSERT_TRUE(map2.IsValid()); + ASSERT_LT(map2a.End(), map2a.BaseEnd()); + ASSERT_EQ(map2a.BaseEnd(), map2.Begin()); + + // And take the rest of the middle reservation. + const size_t kChunk2BSize = map2.Size() - 1u; + uint8_t* addr2b = map2.Begin(); + MemMap map2b = map2.TakeReservedMemory(kChunk2BSize); + ASSERT_TRUE(map2b.IsValid()) << error_msg; + ASSERT_TRUE(error_msg.empty()); + ASSERT_EQ(map2b.Size(), kChunk2ASize); + ASSERT_EQ(addr2b, map2b.Begin()); + ASSERT_FALSE(map2.IsValid()); +} + } // namespace art diff --git a/libartbase/base/zip_archive.cc b/libartbase/base/zip_archive.cc index a841bae8ea..174d22792a 100644 --- a/libartbase/base/zip_archive.cc +++ b/libartbase/base/zip_archive.cc @@ -133,16 +133,14 @@ MemMap ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* erro } MemMap map = - MemMap::MapFileAtAddress(nullptr, // Expected pointer address - GetUncompressedLength(), // Byte count - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - zip_fd, - offset, - false, // Don't restrict allocation to lower4GB - false, // Doesn't overlap existing map (reuse=false) - name.c_str(), - /*out*/error_msg); + MemMap::MapFile(GetUncompressedLength(), // Byte count + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + zip_fd, + offset, + /* low_4gb */ false, + name.c_str(), + error_msg); if (!map.IsValid()) { DCHECK(!error_msg->empty()); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index fdf43d7632..91283d6977 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1825,11 +1825,11 @@ class ImageDumper { oat_file = OatFile::Open(/* zip_fd */ -1, oat_location, oat_location, - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg); } if (oat_file == nullptr) { @@ -2723,11 +2723,11 @@ static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, options->app_oat_, options->app_oat_, - nullptr, - nullptr, - false, - /*low_4gb*/true, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ true, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); if (oat_file == nullptr) { LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg; @@ -2847,11 +2847,11 @@ static int DumpOat(Runtime* runtime, std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, oat_filename, oat_filename, - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_filename, + /* reservation */ nullptr, &error_msg)); if (oat_file == nullptr) { LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg; @@ -2873,11 +2873,11 @@ static int SymbolizeOat(const char* oat_filename, std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, oat_filename, oat_filename, - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_filename, + /* reservation */ nullptr, &error_msg)); if (oat_file == nullptr) { LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg; @@ -2921,11 +2921,11 @@ class IMTDumper { std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, oat_filename, oat_filename, - nullptr, - nullptr, - false, + /* requested_base */ nullptr, + /* executable */ false, /*low_4gb*/false, dex_filename, + /* reservation */ nullptr, &error_msg)); if (oat_file == nullptr) { LOG(ERROR) << "Failed to open oat file from '" << oat_filename << "': " << error_msg; diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 127e14e239..93af77fe23 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -151,11 +151,11 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location, std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; EXPECT_EQ(pic, odex_file->IsPic()); diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index d45a689fd6..e7715c4934 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -37,9 +37,7 @@ namespace art { using android::base::StringPrintf; template <typename ElfTypes> -ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable, - bool program_header_only, - uint8_t* requested_base) +ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable, bool program_header_only) : writable_(writable), program_header_only_(program_header_only), header_(nullptr), @@ -54,8 +52,7 @@ ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable, dynstr_section_start_(nullptr), hash_section_start_(nullptr), symtab_symbol_table_(nullptr), - dynsym_symbol_table_(nullptr), - requested_base_(requested_base) { + dynsym_symbol_table_(nullptr) { CHECK(file != nullptr); } @@ -64,10 +61,9 @@ ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file, bool writable, bool program_header_only, bool low_4gb, - std::string* error_msg, - uint8_t* requested_base) { - std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes> - (file, writable, program_header_only, requested_base)); + std::string* error_msg) { + std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file( + new ElfFileImpl<ElfTypes>(file, writable, program_header_only)); int prot; int flags; if (writable) { @@ -89,9 +85,8 @@ ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file, int flags, bool low_4gb, std::string* error_msg) { - std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes> - (file, (prot & PROT_WRITE) == PROT_WRITE, /*program_header_only*/false, - /*requested_base*/nullptr)); + std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file( + new ElfFileImpl<ElfTypes>(file, (prot & PROT_WRITE) != 0, /* program_header_only */ false)); if (!elf_file->Setup(file, prot, flags, low_4gb, error_msg)) { return nullptr; } @@ -684,9 +679,7 @@ template <typename ElfTypes> typename ElfTypes::Phdr* ElfFileImpl<ElfTypes>::GetProgramHeader(Elf_Word i) const { CHECK_LT(i, GetProgramHeaderNum()) << file_path_; // Sanity check for caller. uint8_t* program_header = GetProgramHeadersStart() + (i * GetHeader().e_phentsize); - if (program_header >= End()) { - return nullptr; // Failure condition. - } + CHECK_LT(program_header, End()); return reinterpret_cast<Elf_Phdr*>(program_header); } @@ -1027,9 +1020,17 @@ typename ElfTypes::Rela& ElfFileImpl<ElfTypes>::GetRela(Elf_Shdr& section_header return *(GetRelaSectionStart(section_header) + i); } -// Base on bionic phdr_table_get_load_size template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) const { + uint8_t* vaddr_begin; + return GetLoadedAddressRange(&vaddr_begin, size, error_msg); +} + +// Base on bionic phdr_table_get_load_size +template <typename ElfTypes> +bool ElfFileImpl<ElfTypes>::GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin, + /*out*/size_t* vaddr_size, + /*out*/std::string* error_msg) const { Elf_Addr min_vaddr = static_cast<Elf_Addr>(-1); Elf_Addr max_vaddr = 0u; for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) { @@ -1048,7 +1049,8 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) << program_header->p_vaddr << "+0x" << program_header->p_memsz << "=0x" << end_vaddr << " in ELF file \"" << file_path_ << "\""; *error_msg = oss.str(); - *size = static_cast<size_t>(-1); + *vaddr_begin = nullptr; + *vaddr_size = static_cast<size_t>(-1); return false; } if (end_vaddr > max_vaddr) { @@ -1058,17 +1060,19 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg) min_vaddr = RoundDown(min_vaddr, kPageSize); max_vaddr = RoundUp(max_vaddr, kPageSize); CHECK_LT(min_vaddr, max_vaddr) << file_path_; - Elf_Addr loaded_size = max_vaddr - min_vaddr; - // Check that the loaded_size fits in size_t. - if (UNLIKELY(loaded_size > std::numeric_limits<size_t>::max())) { + // Check that the range fits into the runtime address space. + if (UNLIKELY(max_vaddr - 1u > std::numeric_limits<size_t>::max())) { std::ostringstream oss; - oss << "Loaded size is 0x" << std::hex << loaded_size << " but maximum size_t is 0x" - << std::numeric_limits<size_t>::max() << " for ELF file \"" << file_path_ << "\""; + oss << "Loaded range is 0x" << std::hex << min_vaddr << "-0x" << max_vaddr + << " but maximum size_t is 0x" << std::numeric_limits<size_t>::max() + << " for ELF file \"" << file_path_ << "\""; *error_msg = oss.str(); - *size = static_cast<size_t>(-1); + *vaddr_begin = nullptr; + *vaddr_size = static_cast<size_t>(-1); return false; } - *size = loaded_size; + *vaddr_begin = reinterpret_cast<uint8_t*>(min_vaddr); + *vaddr_size = dchecked_integral_cast<size_t>(max_vaddr - min_vaddr); return true; } @@ -1099,7 +1103,8 @@ template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::Load(File* file, bool executable, bool low_4gb, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { CHECK(program_header_only_) << file->GetPath(); if (executable) { @@ -1115,11 +1120,6 @@ bool ElfFileImpl<ElfTypes>::Load(File* file, bool reserved = false; for (Elf_Word i = 0; i < GetProgramHeaderNum(); i++) { Elf_Phdr* program_header = GetProgramHeader(i); - if (program_header == nullptr) { - *error_msg = StringPrintf("No program header for entry %d in ELF file %s.", - i, file->GetPath().c_str()); - return false; - } // Record .dynamic header information for later use if (program_header->p_type == PT_DYNAMIC) { @@ -1150,41 +1150,39 @@ bool ElfFileImpl<ElfTypes>::Load(File* file, } size_t file_length = static_cast<size_t>(temp_file_length); if (!reserved) { - uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr); - uint8_t* reserve_base_override = reserve_base; - // Override the base (e.g. when compiling with --compile-pic) - if (requested_base_ != nullptr) { - reserve_base_override = requested_base_; - } - std::string reservation_name("ElfFile reservation for "); - reservation_name += file->GetPath(); - size_t loaded_size; - if (!GetLoadedSize(&loaded_size, error_msg)) { + uint8_t* vaddr_begin; + size_t vaddr_size; + if (!GetLoadedAddressRange(&vaddr_begin, &vaddr_size, error_msg)) { DCHECK(!error_msg->empty()); return false; } - MemMap reserve = MemMap::MapAnonymous(reservation_name.c_str(), - reserve_base_override, - loaded_size, - PROT_NONE, - low_4gb, - error_msg); - if (!reserve.IsValid()) { + std::string reservation_name = "ElfFile reservation for " + file->GetPath(); + MemMap local_reservation = MemMap::MapAnonymous( + reservation_name.c_str(), + (reservation != nullptr) ? reservation->Begin() : nullptr, + vaddr_size, + PROT_NONE, + low_4gb, + /* reuse */ false, + reservation, + error_msg); + if (!local_reservation.IsValid()) { *error_msg = StringPrintf("Failed to allocate %s: %s", - reservation_name.c_str(), error_msg->c_str()); + reservation_name.c_str(), + error_msg->c_str()); return false; } reserved = true; - // Base address is the difference of actual mapped location and the p_vaddr - base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve.Begin()) - - reinterpret_cast<uintptr_t>(reserve_base)); + // Base address is the difference of actual mapped location and the vaddr_begin. + base_address_ = reinterpret_cast<uint8_t*>( + static_cast<uintptr_t>(local_reservation.Begin() - vaddr_begin)); // By adding the p_vaddr of a section/symbol to base_address_ we will always get the // dynamic memory address of where that object is actually mapped // // TODO: base_address_ needs to be calculated in ::Open, otherwise // FindDynamicSymbolAddress returns the wrong values until Load is called. - segments_.push_back(std::move(reserve)); + segments_.push_back(std::move(local_reservation)); } // empty segment, nothing to map if (program_header->p_memsz == 0) { @@ -1239,9 +1237,10 @@ bool ElfFileImpl<ElfTypes>::Load(File* file, flags, file->Fd(), program_header->p_offset, - /*low4_gb*/false, - /*reuse*/true, // implies MAP_FIXED + /* low4_gb */ false, file->GetPath().c_str(), + /* reuse */ true, // implies MAP_FIXED + /* reservation */ nullptr, error_msg); if (!segment.IsValid()) { *error_msg = StringPrintf("Failed to map ELF file segment %d from %s: %s", @@ -1265,6 +1264,7 @@ bool ElfFileImpl<ElfTypes>::Load(File* file, prot, /* low_4gb */ false, /* reuse */ true, + /* reservation */ nullptr, error_msg); if (!segment.IsValid()) { *error_msg = StringPrintf("Failed to map zero-initialized ELF file segment %d from %s: %s", @@ -1704,8 +1704,7 @@ ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, bool low_4gb, - std::string* error_msg, - uint8_t* requested_base) { + /*out*/std::string* error_msg) { if (file->GetLength() < EI_NIDENT) { *error_msg = StringPrintf("File %s is too short to be a valid ELF file", file->GetPath().c_str()); @@ -1728,8 +1727,7 @@ ElfFile* ElfFile::Open(File* file, writable, program_header_only, low_4gb, - error_msg, - requested_base); + error_msg); if (elf_file_impl == nullptr) { return nullptr; } @@ -1739,8 +1737,7 @@ ElfFile* ElfFile::Open(File* file, writable, program_header_only, low_4gb, - error_msg, - requested_base); + error_msg); if (elf_file_impl == nullptr) { return nullptr; } @@ -1754,7 +1751,7 @@ ElfFile* ElfFile::Open(File* file, } } -ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg) { +ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, /*out*/std::string* error_msg) { // low_4gb support not required for this path. constexpr bool low_4gb = false; if (file->GetLength() < EI_NIDENT) { @@ -1811,8 +1808,12 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e return elf32_->func(__VA_ARGS__); \ } -bool ElfFile::Load(File* file, bool executable, bool low_4gb, std::string* error_msg) { - DELEGATE_TO_IMPL(Load, file, executable, low_4gb, error_msg); +bool ElfFile::Load(File* file, + bool executable, + bool low_4gb, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { + DELEGATE_TO_IMPL(Load, file, executable, low_4gb, reservation, error_msg); } const uint8_t* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const { diff --git a/runtime/elf_file.h b/runtime/elf_file.h index ab9e6fa2ec..8da7e1a29c 100644 --- a/runtime/elf_file.h +++ b/runtime/elf_file.h @@ -26,6 +26,9 @@ #include "./elf.h" namespace art { + +class MemMap; + template <typename ElfTypes> class ElfFileImpl; @@ -42,18 +45,21 @@ class ElfFile { bool writable, bool program_header_only, bool low_4gb, - std::string* error_msg, - uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg. + /*out*/std::string* error_msg); // Open with specific mmap flags, Always maps in the whole file, not just the // program header sections. static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, - std::string* error_msg); + /*out*/std::string* error_msg); ~ElfFile(); // Load segments into memory based on PT_LOAD program headers - bool Load(File* file, bool executable, bool low_4gb, std::string* error_msg); + bool Load(File* file, + bool executable, + bool low_4gb, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg); const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const; diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 58c38a4436..b55b60f2dc 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -48,13 +48,12 @@ class ElfFileImpl { bool writable, bool program_header_only, bool low_4gb, - std::string* error_msg, - uint8_t* requested_base = nullptr); + /*out*/std::string* error_msg); static ElfFileImpl* Open(File* file, int mmap_prot, int mmap_flags, bool low_4gb, - std::string* error_msg); + /*out*/std::string* error_msg); ~ElfFileImpl(); const std::string& GetFilePath() const { @@ -115,7 +114,11 @@ class ElfFileImpl { // Load segments into memory based on PT_LOAD program headers. // executable is true at run time, false at compile time. - bool Load(File* file, bool executable, bool low_4gb, std::string* error_msg); + bool Load(File* file, + bool executable, + bool low_4gb, + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg); bool Fixup(Elf_Addr base_address); bool FixupDynamic(Elf_Addr base_address); @@ -131,7 +134,11 @@ class ElfFileImpl { bool Strip(File* file, std::string* error_msg); private: - ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base); + ElfFileImpl(File* file, bool writable, bool program_header_only); + + bool GetLoadedAddressRange(/*out*/uint8_t** vaddr_begin, + /*out*/size_t* vaddr_size, + /*out*/std::string* error_msg) const; bool Setup(File* file, int prot, int flags, bool low_4gb, std::string* error_msg); @@ -217,9 +224,6 @@ class ElfFileImpl { SymbolTable* symtab_symbol_table_; SymbolTable* dynsym_symbol_table_; - // Override the 'base' p_vaddr in the first LOAD segment with this value (if non-null). - uint8_t* requested_base_; - DISALLOW_COPY_AND_ASSIGN(ElfFileImpl); }; diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index 677e3f3a05..7bd87bda7a 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -41,13 +41,13 @@ class DummyOatFile : public OatFile { class DummyImageSpace : public space::ImageSpace { public: DummyImageSpace(MemMap&& map, - accounting::ContinuousSpaceBitmap* live_bitmap, + std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap, std::unique_ptr<DummyOatFile>&& oat_file, MemMap&& oat_map) : ImageSpace("DummyImageSpace", /*image_location*/"", std::move(map), - live_bitmap, + std::move(live_bitmap), map.End()), oat_map_(std::move(oat_map)) { oat_file_ = std::move(oat_file); @@ -130,7 +130,7 @@ class ImmuneSpacesTest : public CommonRuntimeTest { ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); return new DummyImageSpace(std::move(map), - live_bitmap.release(), + std::move(live_bitmap), std::move(oat_file), std::move(oat_map)); } diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 71786279e2..0af049a7a8 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -63,7 +63,7 @@ Atomic<uint32_t> ImageSpace::bitmap_index_(0); ImageSpace::ImageSpace(const std::string& image_filename, const char* image_location, MemMap&& mem_map, - accounting::ContinuousSpaceBitmap* live_bitmap, + std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap, uint8_t* end) : MemMapSpace(image_filename, std::move(mem_map), @@ -71,10 +71,10 @@ ImageSpace::ImageSpace(const std::string& image_filename, end, end, kGcRetentionPolicyNeverCollect), + live_bitmap_(std::move(live_bitmap)), oat_file_non_owned_(nullptr), image_location_(image_location) { - DCHECK(live_bitmap != nullptr); - live_bitmap_.reset(live_bitmap); + DCHECK(live_bitmap_ != nullptr); } static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { @@ -480,52 +480,16 @@ std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) { } // Helper class encapsulating loading, so we can access private ImageSpace members (this is a -// friend class), but not declare functions in the header. +// nested class), but not declare functions in the header. class ImageSpace::Loader { public: - static std::unique_ptr<ImageSpace> Load(const std::string& image_location, - const std::string& image_filename, - bool is_zygote, - bool is_global_cache, - bool validate_oat_file, - std::string* error_msg) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Should this be a RDWR lock? This is only a defensive measure, as at - // this point the image should exist. - // However, only the zygote can write into the global dalvik-cache, so - // restrict to zygote processes, or any process that isn't using - // /data/dalvik-cache (which we assume to be allowed to write there). - const bool rw_lock = is_zygote || !is_global_cache; - - // Note that we must not use the file descriptor associated with - // ScopedFlock::GetFile to Init the image file. We want the file - // descriptor (and the associated exclusive lock) to be released when - // we leave Create. - ScopedFlock image = LockedFile::Open(image_filename.c_str(), - rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */, - true /* block */, - error_msg); - - VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location " - << image_location; - // If we are in /system we can assume the image is good. We can also - // assume this if we are using a relocated image (i.e. image checksum - // matches) since this is only different by the offset. We need this to - // make sure that host tests continue to work. - // Since we are the boot image, pass null since we load the oat file from the boot image oat - // file name. - return Init(image_filename.c_str(), - image_location.c_str(), - validate_oat_file, - /* oat_file */nullptr, - error_msg); - } - static std::unique_ptr<ImageSpace> Init(const char* image_filename, const char* image_location, bool validate_oat_file, const OatFile* oat_file, - std::string* error_msg) + /*inout*/MemMap* image_reservation, + /*inout*/MemMap* oat_reservation, + /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { CHECK(image_filename != nullptr); CHECK(image_location != nullptr); @@ -616,22 +580,27 @@ class ImageSpace::Loader { // image at the image begin, the amount of fixup work required is minimized. // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to // avoid reading proc maps for a mapping failure and slowing everything down. - map = LoadImageFile(image_filename, - image_location, - *image_header, - image_header->GetImageBegin(), - file->Fd(), - logger, - image_header->IsPic() ? nullptr : error_msg); + // For the boot image, we have already reserved the memory and we load the image + // into the `image_reservation`. + map = LoadImageFile( + image_filename, + image_location, + *image_header, + image_header->GetImageBegin(), + file->Fd(), + logger, + image_reservation, + (image_reservation == nullptr && image_header->IsPic()) ? nullptr : error_msg); // If the header specifies PIC mode, we can also map at a random low_4gb address since we can // relocate in-place. - if (!map.IsValid() && image_header->IsPic()) { + if (!map.IsValid() && image_reservation == nullptr && image_header->IsPic()) { map = LoadImageFile(image_filename, image_location, *image_header, /* address */ nullptr, file->Fd(), logger, + /* image_reservation */ nullptr, error_msg); } // Were we able to load something and continue? @@ -641,15 +610,13 @@ class ImageSpace::Loader { } DCHECK_EQ(0, memcmp(image_header, map.Begin(), sizeof(ImageHeader))); - MemMap image_bitmap_map = MemMap::MapFileAtAddress(nullptr, - bitmap_section.Size(), - PROT_READ, MAP_PRIVATE, - file->Fd(), - image_bitmap_offset, - /*low_4gb*/false, - /*reuse*/false, - image_filename, - error_msg); + MemMap image_bitmap_map = MemMap::MapFile(bitmap_section.Size(), + PROT_READ, MAP_PRIVATE, + file->Fd(), + image_bitmap_offset, + /* low_4gb */ false, + image_filename, + error_msg); if (!image_bitmap_map.IsValid()) { *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str()); return nullptr; @@ -694,7 +661,7 @@ class ImageSpace::Loader { std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location, std::move(map), - bitmap.release(), + std::move(bitmap), image_end)); // VerifyImageAllocations() will be called later in Runtime::Init() @@ -704,7 +671,7 @@ class ImageSpace::Loader { // set yet at this point. if (oat_file == nullptr) { TimingLogger::ScopedTiming timing("OpenOatFile", &logger); - space->oat_file_ = OpenOatFile(*space, image_filename, error_msg); + space->oat_file_ = OpenOatFile(*space, image_filename, oat_reservation, error_msg); if (space->oat_file_ == nullptr) { DCHECK(!error_msg->empty()); return nullptr; @@ -787,7 +754,8 @@ class ImageSpace::Loader { uint8_t* address, int fd, TimingLogger& logger, - std::string* error_msg) { + /*inout*/MemMap* image_reservation, + /*out*/std::string* error_msg) { TimingLogger::ScopedTiming timing("MapImageFile", &logger); const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode(); if (storage_mode == ImageHeader::kStorageModeUncompressed) { @@ -796,10 +764,11 @@ class ImageSpace::Loader { PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, - 0, - /*low_4gb*/true, - /*reuse*/false, + /* start */ 0, + /* low_4gb */ true, image_filename, + /* reuse */ false, + image_reservation, error_msg); } @@ -817,7 +786,9 @@ class ImageSpace::Loader { address, image_header.GetImageSize(), PROT_READ | PROT_WRITE, - /*low_4gb*/ true, + /* low_4gb */ true, + /* reuse */ false, + image_reservation, error_msg); if (map.IsValid()) { const size_t stored_size = image_header.GetDataSize(); @@ -826,8 +797,8 @@ class ImageSpace::Loader { PROT_READ, MAP_PRIVATE, fd, - /*offset*/0, - /*low_4gb*/false, + /* offset */ 0, + /* low_4gb */ false, image_filename, error_msg); if (!temp_map.IsValid()) { @@ -1396,6 +1367,7 @@ class ImageSpace::Loader { static std::unique_ptr<OatFile> OpenOatFile(const ImageSpace& image, const char* image_path, + /*inout*/MemMap* oat_reservation, std::string* error_msg) { const ImageHeader& image_header = image.GetImageHeader(); std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path); @@ -1406,10 +1378,10 @@ class ImageSpace::Loader { oat_filename, oat_filename, image_header.GetOatDataBegin(), - image_header.GetOatFileBegin(), !Runtime::Current()->IsAotCompiler(), - /*low_4gb*/false, - nullptr, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + oat_reservation, error_msg)); if (oat_file == nullptr) { *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s", @@ -1497,20 +1469,48 @@ class ImageSpace::BootImageLoader { if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) { return false; } + uint32_t image_start; + uint32_t image_end; + uint32_t oat_end; + if (!GetBootImageAddressRange(filename, &image_start, &image_end, &oat_end, error_msg)) { + return false; + } + if (locations.size() > 1u) { + std::string last_filename = GetSystemImageFilename(locations.back().c_str(), image_isa_); + uint32_t dummy; + if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) { + return false; + } + } + MemMap image_reservation; + MemMap oat_reservation; + if (!ReserveBootImageMemory(image_start, + image_end, + oat_end, + &image_reservation, + &oat_reservation, + error_msg)) { + return false; + } + std::vector<std::unique_ptr<ImageSpace>> spaces; spaces.reserve(locations.size()); for (const std::string& location : locations) { filename = GetSystemImageFilename(location.c_str(), image_isa_); - spaces.push_back(Loader::Load(location, - filename, - is_zygote_, - is_global_cache_, - /* validate_oat_file */ false, - error_msg)); + spaces.push_back(Load(location, + filename, + /* validate_oat_file */ false, + &image_reservation, + &oat_reservation, + error_msg)); if (spaces.back() == nullptr) { return false; } } + if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) { + return false; + } + *oat_file_end = GetOatFileEnd(spaces); boot_image_spaces->swap(spaces); return true; @@ -1527,6 +1527,36 @@ class ImageSpace::BootImageLoader { if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) { return false; } + uint32_t image_start; + uint32_t image_end; + uint32_t oat_end; + if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, &oat_end, error_msg)) { + return false; + } + if (locations.size() > 1u) { + std::string last_filename; + if (!GetDalvikCacheFilename(locations.back().c_str(), + dalvik_cache_.c_str(), + &last_filename, + error_msg)) { + return false; + } + uint32_t dummy; + if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) { + return false; + } + } + MemMap image_reservation; + MemMap oat_reservation; + if (!ReserveBootImageMemory(image_start, + image_end, + oat_end, + &image_reservation, + &oat_reservation, + error_msg)) { + return false; + } + std::vector<std::unique_ptr<ImageSpace>> spaces; spaces.reserve(locations.size()); for (const std::string& location : locations) { @@ -1534,12 +1564,12 @@ class ImageSpace::BootImageLoader { if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) { return false; } - spaces.push_back(Loader::Load(location, - filename, - is_zygote_, - is_global_cache_, - validate_oat_file, - error_msg)); + spaces.push_back(Load(location, + filename, + validate_oat_file, + &image_reservation, + &oat_reservation, + error_msg)); if (spaces.back() == nullptr) { return false; } @@ -1560,12 +1590,56 @@ class ImageSpace::BootImageLoader { } } } + if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) { + return false; + } + *oat_file_end = GetOatFileEnd(spaces); boot_image_spaces->swap(spaces); return true; } private: + std::unique_ptr<ImageSpace> Load(const std::string& image_location, + const std::string& image_filename, + bool validate_oat_file, + /*inout*/MemMap* image_reservation, + /*inout*/MemMap* oat_reservation, + /*out*/std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Should this be a RDWR lock? This is only a defensive measure, as at + // this point the image should exist. + // However, only the zygote can write into the global dalvik-cache, so + // restrict to zygote processes, or any process that isn't using + // /data/dalvik-cache (which we assume to be allowed to write there). + const bool rw_lock = is_zygote_ || !is_global_cache_; + + // Note that we must not use the file descriptor associated with + // ScopedFlock::GetFile to Init the image file. We want the file + // descriptor (and the associated exclusive lock) to be released when + // we leave Create. + ScopedFlock image = LockedFile::Open(image_filename.c_str(), + rw_lock ? (O_CREAT | O_RDWR) : O_RDONLY /* flags */, + true /* block */, + error_msg); + + VLOG(startup) << "Using image file " << image_filename.c_str() << " for image location " + << image_location; + // If we are in /system we can assume the image is good. We can also + // assume this if we are using a relocated image (i.e. image checksum + // matches) since this is only different by the offset. We need this to + // make sure that host tests continue to work. + // Since we are the boot image, pass null since we load the oat file from the boot image oat + // file name. + return Loader::Init(image_filename.c_str(), + image_location.c_str(), + validate_oat_file, + /* oat_file */ nullptr, + image_reservation, + oat_reservation, + error_msg); + } + // Extract boot class path from oat file associated with `image_filename` // and list all associated image locations. static bool GetBootClassPathImageLocations(const std::string& image_location, @@ -1577,10 +1651,10 @@ class ImageSpace::BootImageLoader { oat_filename, oat_filename, /* requested_base */ nullptr, - /* oat_file_begin */ nullptr, /* executable */ false, /* low_4gb */ false, /* abs_dex_location */ nullptr, + /* reservation */ nullptr, error_msg)); if (oat_file == nullptr) { *error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s", @@ -1598,6 +1672,72 @@ class ImageSpace::BootImageLoader { return true; } + bool GetBootImageAddressRange(const std::string& filename, + /*out*/uint32_t* start, + /*out*/uint32_t* end, + /*out*/uint32_t* oat_end, + /*out*/std::string* error_msg) { + ImageHeader system_hdr; + if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) { + *error_msg = StringPrintf("Cannot read header of %s", filename.c_str()); + return false; + } + *start = reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()); + CHECK_ALIGNED(*start, kPageSize); + *end = RoundUp(*start + system_hdr.GetImageSize(), kPageSize); + *oat_end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize); + return true; + } + + bool ReserveBootImageMemory(uint32_t image_start, + uint32_t image_end, + uint32_t oat_end, + /*out*/MemMap* image_reservation, + /*out*/MemMap* oat_reservation, + /*out*/std::string* error_msg) { + DCHECK(!image_reservation->IsValid()); + *image_reservation = + MemMap::MapAnonymous("Boot image reservation", + reinterpret_cast32<uint8_t*>(image_start), + oat_end - image_start, + PROT_NONE, + /* low_4gb */ true, + /* reuse */ false, + /* reservation */ nullptr, + error_msg); + if (!image_reservation->IsValid()) { + return false; + } + DCHECK(!oat_reservation->IsValid()); + *oat_reservation = image_reservation->RemapAtEnd(reinterpret_cast32<uint8_t*>(image_end), + "Boot image oat reservation", + PROT_NONE, + error_msg); + if (!oat_reservation->IsValid()) { + return false; + } + + return true; + } + + bool CheckReservationsExhausted(const MemMap& image_reservation, + const MemMap& oat_reservation, + /*out*/std::string* error_msg) { + if (image_reservation.IsValid()) { + *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p", + image_reservation.Begin(), + image_reservation.End()); + return false; + } + if (oat_reservation.IsValid()) { + *error_msg = StringPrintf("Excessive oat reservation after loading boot image: %p-%p", + image_reservation.Begin(), + image_reservation.End()); + return false; + } + return true; + } + uint8_t* GetOatFileEnd(const std::vector<std::unique_ptr<ImageSpace>>& spaces) { DCHECK(std::is_sorted( spaces.begin(), @@ -1834,7 +1974,13 @@ ImageSpace::~ImageSpace() { std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image, const OatFile* oat_file, std::string* error_msg) { - return Loader::Init(image, image, /*validate_oat_file*/false, oat_file, /*out*/error_msg); + return Loader::Init(image, + image, + /* validate_oat_file */ false, + oat_file, + /* image_reservation */ nullptr, + /* oat_reservation */ nullptr, + error_msg); } const OatFile* ImageSpace::GetOatFile() const { diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 93cf947218..bd686be62e 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -183,7 +183,7 @@ class ImageSpace : public MemMapSpace { ImageSpace(const std::string& name, const char* image_location, MemMap&& mem_map, - accounting::ContinuousSpaceBitmap* live_bitmap, + std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap, uint8_t* end); // The OatFile associated with the image during early startup to diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 347af4e1fd..299a413432 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -46,11 +46,11 @@ TEST_F(DexoptTest, ValidateOatFile) { std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - nullptr, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, + /* abs_dex_location */ nullptr, + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(oat != nullptr) << error_msg; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index b743b40330..2b2898c195 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -207,6 +207,7 @@ JitCodeCache* JitCodeCache::Create(size_t initial_capacity, kProtData, /* low_4gb */ true, /* reuse */ false, + /* reservation */ nullptr, &error_str); if (!data_map.IsValid()) { std::ostringstream oss; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 4780aea003..d5246b45f0 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -102,12 +102,12 @@ class OatFileBase : public OatFile { const std::string& elf_filename, const std::string& location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); template <typename kOatFileBaseSubType> static OatFileBase* OpenOatFile(int zip_fd, @@ -116,12 +116,12 @@ class OatFileBase : public OatFile { const std::string& vdex_filename, const std::string& oat_filename, uint8_t* requested_base, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); protected: OatFileBase(const std::string& filename, bool executable) : OatFile(filename, executable) {} @@ -143,18 +143,18 @@ class OatFileBase : public OatFile { std::string* error_msg); virtual bool Load(const std::string& elf_filename, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, - std::string* error_msg) = 0; + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) = 0; virtual bool Load(int oat_fd, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, - std::string* error_msg) = 0; + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) = 0; bool ComputeFields(uint8_t* requested_base, const std::string& file_path, @@ -188,21 +188,21 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, const std::string& elf_filename, const std::string& location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(location, executable)); ret->PreLoad(); if (!ret->Load(elf_filename, - oat_file_begin, writable, executable, low_4gb, + reservation, error_msg)) { return nullptr; } @@ -231,19 +231,19 @@ OatFileBase* OatFileBase::OpenOatFile(int zip_fd, const std::string& vdex_location, const std::string& oat_location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable)); if (!ret->Load(oat_fd, - oat_file_begin, writable, executable, low_4gb, + reservation, error_msg)) { return nullptr; } @@ -923,13 +923,18 @@ class DlOpenOatFile final : public OatFileBase { void PreLoad() override; bool Load(const std::string& elf_filename, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, - std::string* error_msg) override; - - bool Load(int, uint8_t*, bool, bool, bool, std::string*) { + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) override; + + bool Load(int oat_fd ATTRIBUTE_UNUSED, + bool writable ATTRIBUTE_UNUSED, + bool executable ATTRIBUTE_UNUSED, + bool low_4gb ATTRIBUTE_UNUSED, + /*inout*/MemMap* reservation ATTRIBUTE_UNUSED, + /*out*/std::string* error_msg ATTRIBUTE_UNUSED) override { return false; } @@ -938,8 +943,8 @@ class DlOpenOatFile final : public OatFileBase { private: bool Dlopen(const std::string& elf_filename, - uint8_t* oat_file_begin, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); // On the host, if the same library is loaded again with dlopen the same // file handle is returned. This differs from the behavior of dlopen on the @@ -952,12 +957,13 @@ class DlOpenOatFile final : public OatFileBase { // Guarded by host_dlopen_handles_lock_; static std::unordered_set<void*> host_dlopen_handles_; + // Reservation and dummy memory map objects corresponding to the regions mapped by dlopen. + // Note: Must be destroyed after dlclose() as it can hold the owning reservation. + std::vector<MemMap> dlopen_mmaps_; + // dlopen handle during runtime. void* dlopen_handle_; // TODO: Unique_ptr with custom deleter. - // Dummy memory map objects corresponding to the regions mapped by dlopen. - std::vector<MemMap> dlopen_mmaps_; - // The number of shared objects the linker told us about before loading. Used to // (optimistically) optimize the PreSetup stage (see comment there). size_t shared_objects_before_; @@ -975,9 +981,9 @@ void DlOpenOatFile::PreLoad() { #else // Count the entries in dl_iterate_phdr we get at this point in time. struct dl_iterate_context { - static int callback(struct dl_phdr_info *info ATTRIBUTE_UNUSED, + static int callback(dl_phdr_info* info ATTRIBUTE_UNUSED, size_t size ATTRIBUTE_UNUSED, - void *data) { + void* data) { reinterpret_cast<dl_iterate_context*>(data)->count++; return 0; // Continue iteration. } @@ -990,11 +996,11 @@ void DlOpenOatFile::PreLoad() { } bool DlOpenOatFile::Load(const std::string& elf_filename, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, - std::string* error_msg) { + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) { // Use dlopen only when flagged to do so, and when it's OK to load things executable. // TODO: Also try when not executable? The issue here could be re-mapping as writable (as // !executable is a sign that we may want to patch), which may not be allowed for @@ -1027,15 +1033,15 @@ bool DlOpenOatFile::Load(const std::string& elf_filename, } } - bool success = Dlopen(elf_filename, oat_file_begin, error_msg); + bool success = Dlopen(elf_filename, reservation, error_msg); DCHECK(dlopen_handle_ != nullptr || !success); return success; } bool DlOpenOatFile::Dlopen(const std::string& elf_filename, - uint8_t* oat_file_begin, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { #ifdef __APPLE__ // The dl_iterate_phdr syscall is missing. There is similar API on OSX, // but let's fallback to the custom loading code for the time being. @@ -1056,15 +1062,85 @@ bool DlOpenOatFile::Dlopen(const std::string& elf_filename, // times). ANDROID_DLEXT_FORCE_FIXED_VADDR; // Take a non-zero vaddr as absolute // (non-pic boot image). - if (oat_file_begin != nullptr) { // - extinfo.flags |= ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS; // Use the requested addr if - extinfo.reserved_addr = oat_file_begin; // vaddr = 0. - } // (pic boot image). + if (reservation != nullptr) { + if (!reservation->IsValid()) { + *error_msg = StringPrintf("Invalid reservation for %s", elf_filename.c_str()); + return false; + } + extinfo.flags |= ANDROID_DLEXT_RESERVED_ADDRESS; // Use the reserved memory range. + extinfo.reserved_addr = reservation->Begin(); + extinfo.reserved_size = reservation->Size(); + } dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo); + if (reservation != nullptr && dlopen_handle_ != nullptr) { + // Find used pages from the reservation. + struct dl_iterate_context { + static int callback(dl_phdr_info* info, size_t size ATTRIBUTE_UNUSED, void* data) { + auto* context = reinterpret_cast<dl_iterate_context*>(data); + static_assert(std::is_same<Elf32_Half, Elf64_Half>::value, "Half must match"); + using Elf_Half = Elf64_Half; + + // See whether this callback corresponds to the file which we have just loaded. + uint8_t* reservation_begin = context->reservation->Begin(); + bool contained_in_reservation = false; + for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr + + info->dlpi_phdr[i].p_vaddr); + size_t memsz = info->dlpi_phdr[i].p_memsz; + size_t offset = static_cast<size_t>(vaddr - reservation_begin); + if (offset < context->reservation->Size()) { + contained_in_reservation = true; + DCHECK_LE(memsz, context->reservation->Size() - offset); + } else if (vaddr < reservation_begin) { + // Check that there's no overlap with the reservation. + DCHECK_LE(memsz, static_cast<size_t>(reservation_begin - vaddr)); + } + break; // It is sufficient to check the first PT_LOAD header. + } + } + + if (contained_in_reservation) { + for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr + + info->dlpi_phdr[i].p_vaddr); + size_t memsz = info->dlpi_phdr[i].p_memsz; + size_t offset = static_cast<size_t>(vaddr - reservation_begin); + DCHECK_LT(offset, context->reservation->Size()); + DCHECK_LE(memsz, context->reservation->Size() - offset); + context->max_size = std::max(context->max_size, offset + memsz); + } + } + + return 1; // Stop iteration and return 1 from dl_iterate_phdr. + } + return 0; // Continue iteration and return 0 from dl_iterate_phdr when finished. + } + + const MemMap* const reservation; + size_t max_size = 0u; + }; + dl_iterate_context context = { reservation }; + + if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) { + LOG(FATAL) << "Could not find the shared object mmapped to the reservation."; + UNREACHABLE(); + } + + // Take ownership of the memory used by the shared object. dlopen() does not assume + // full ownership of this memory and dlclose() shall just remap it as zero pages with + // PROT_NONE. We need to unmap the memory when destroying this oat file. + dlopen_mmaps_.push_back(reservation->TakeReservedMemory(context.max_size)); + } #else - UNUSED(oat_file_begin); static_assert(!kIsTargetBuild || kIsTargetLinux || kIsTargetFuchsia, "host_dlopen_handles_ will leak handles"); + if (reservation != nullptr) { + *error_msg = StringPrintf("dlopen() into reserved memory is unsupported on host for '%s'.", + elf_filename.c_str()); + return false; + } MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_); dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW); if (dlopen_handle_ != nullptr) { @@ -1092,8 +1168,11 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) { UNREACHABLE(); #else struct dl_iterate_context { - static int callback(struct dl_phdr_info *info, size_t /* size */, void *data) { + static int callback(dl_phdr_info* info, size_t size ATTRIBUTE_UNUSED, void* data) { auto* context = reinterpret_cast<dl_iterate_context*>(data); + static_assert(std::is_same<Elf32_Half, Elf64_Half>::value, "Half must match"); + using Elf_Half = Elf64_Half; + context->shared_objects_seen++; if (context->shared_objects_seen < context->shared_objects_before) { // We haven't been called yet for anything we haven't seen before. Just continue. @@ -1104,7 +1183,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) { // See whether this callback corresponds to the file which we have just loaded. bool contains_begin = false; - for (int i = 0; i < info->dlpi_phnum; i++) { + for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) { uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); @@ -1117,7 +1196,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) { } // Add dummy mmaps for this file. if (contains_begin) { - for (int i = 0; i < info->dlpi_phnum; i++) { + for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) { uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); @@ -1163,13 +1242,12 @@ class ElfOatFile final : public OatFileBase { static ElfOatFile* OpenElfFile(int zip_fd, File* file, const std::string& location, - uint8_t* requested_base, - uint8_t* oat_file_begin, // Override base if not null bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); bool InitializeFromElfFile(int zip_fd, ElfFile* elf_file, @@ -1191,29 +1269,29 @@ class ElfOatFile final : public OatFileBase { } bool Load(const std::string& elf_filename, - uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, bool low_4gb, - std::string* error_msg) override; + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) override; bool Load(int oat_fd, - uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, bool low_4gb, - std::string* error_msg) override; + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) override; void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) override { } private: bool ElfFileOpen(File* file, - uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, bool low_4gb, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); private: // Backing memory map for oat file during cross compilation. @@ -1225,20 +1303,19 @@ class ElfOatFile final : public OatFileBase { ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd, File* file, const std::string& location, - uint8_t* requested_base, - uint8_t* oat_file_begin, // Override base if not null bool writable, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg) { + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg) { ScopedTrace trace("Open elf file " + location); std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, executable)); bool success = oat_file->ElfFileOpen(file, - oat_file_begin, writable, low_4gb, executable, + reservation, error_msg); if (!success) { CHECK(!error_msg->empty()); @@ -1246,7 +1323,7 @@ ElfOatFile* ElfOatFile::OpenElfFile(int zip_fd, } // Complete the setup. - if (!oat_file->ComputeFields(requested_base, file->GetPath(), error_msg)) { + if (!oat_file->ComputeFields(/* requested_base */ nullptr, file->GetPath(), error_msg)) { return nullptr; } @@ -1279,11 +1356,11 @@ bool ElfOatFile::InitializeFromElfFile(int zip_fd, } bool ElfOatFile::Load(const std::string& elf_filename, - uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, bool low_4gb, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { ScopedTrace trace(__PRETTY_FUNCTION__); std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str())); if (file == nullptr) { @@ -1291,19 +1368,19 @@ bool ElfOatFile::Load(const std::string& elf_filename, return false; } return ElfOatFile::ElfFileOpen(file.get(), - oat_file_begin, writable, executable, low_4gb, + reservation, error_msg); } bool ElfOatFile::Load(int oat_fd, - uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, bool low_4gb, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { ScopedTrace trace(__PRETTY_FUNCTION__); if (oat_fd != -1) { std::unique_ptr<File> file = std::make_unique<File>(oat_fd, false); @@ -1314,34 +1391,33 @@ bool ElfOatFile::Load(int oat_fd, return false; } return ElfOatFile::ElfFileOpen(file.get(), - oat_file_begin, writable, executable, low_4gb, + reservation, error_msg); } return false; } bool ElfOatFile::ElfFileOpen(File* file, - uint8_t* oat_file_begin, bool writable, bool executable, bool low_4gb, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { ScopedTrace trace(__PRETTY_FUNCTION__); // TODO: rename requested_base to oat_data_begin elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, low_4gb, - error_msg, - oat_file_begin)); + error_msg)); if (elf_file_ == nullptr) { DCHECK(!error_msg->empty()); return false; } - bool loaded = elf_file_->Load(file, executable, low_4gb, error_msg); + bool loaded = elf_file_->Load(file, executable, low_4gb, reservation, error_msg); DCHECK(loaded || !error_msg->empty()); return loaded; } @@ -1392,11 +1468,11 @@ OatFile* OatFile::Open(int zip_fd, const std::string& oat_filename, const std::string& oat_location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { ScopedTrace trace("Open oat file " + oat_location); CHECK(!oat_filename.empty()) << oat_location; CheckLocation(oat_location); @@ -1419,11 +1495,11 @@ OatFile* OatFile::Open(int zip_fd, oat_filename, oat_location, requested_base, - oat_file_begin, false /* writable */, executable, low_4gb, abs_dex_location, + reservation, error_msg); if (with_dlopen != nullptr) { return with_dlopen; @@ -1449,11 +1525,11 @@ OatFile* OatFile::Open(int zip_fd, oat_filename, oat_location, requested_base, - oat_file_begin, false /* writable */, executable, low_4gb, abs_dex_location, + reservation, error_msg); return with_internal; } @@ -1463,11 +1539,11 @@ OatFile* OatFile::Open(int zip_fd, int oat_fd, const std::string& oat_location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg) { + /*inout*/MemMap* reservation, + /*out*/std::string* error_msg) { CHECK(!oat_location.empty()) << oat_location; std::string vdex_location = GetVdexFilename(oat_location); @@ -1478,11 +1554,11 @@ OatFile* OatFile::Open(int zip_fd, vdex_location, oat_location, requested_base, - oat_file_begin, false /* writable */, executable, low_4gb, abs_dex_location, + reservation, error_msg); return with_internal; } @@ -1496,12 +1572,11 @@ OatFile* OatFile::OpenWritable(int zip_fd, return ElfOatFile::OpenElfFile(zip_fd, file, location, - nullptr, - nullptr, - true, - false, + /* writable */ true, + /* executable */ false, /*low_4gb*/false, abs_dex_location, + /* reservation */ nullptr, error_msg); } @@ -1514,12 +1589,11 @@ OatFile* OatFile::OpenReadable(int zip_fd, return ElfOatFile::OpenElfFile(zip_fd, file, location, - nullptr, - nullptr, - false, - false, + /* writable */ false, + /* executable */ false, /*low_4gb*/false, abs_dex_location, + /* reservation */ nullptr, error_msg); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 21e214408d..f20c603bf2 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -86,11 +86,11 @@ class OatFile { const std::string& filename, const std::string& location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); // Similar to OatFile::Open(const std::string...), but accepts input vdex and // odex files as file descriptors. We also take zip_fd in case the vdex does not @@ -100,11 +100,11 @@ class OatFile { int oat_fd, const std::string& oat_location, uint8_t* requested_base, - uint8_t* oat_file_begin, bool executable, bool low_4gb, const char* abs_dex_location, - std::string* error_msg); + /*inout*/MemMap* reservation, // Where to load if not null. + /*out*/std::string* error_msg); // Open an oat file from an already opened File. // Does not use dlopen underneath so cannot be used for runtime use diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index e262ff7150..4ed7e35eee 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -831,22 +831,22 @@ const OatFile* OatFileAssistant::OatFileInfo::GetFile() { vdex_fd_, oat_fd_, filename_.c_str(), - nullptr, - nullptr, + /* requested_base */ nullptr, executable, - false /* low_4gb */, + /* low_4gb */ false, oat_file_assistant_->dex_location_.c_str(), + /* reservation */ nullptr, &error_msg)); } } else { file_.reset(OatFile::Open(/* zip_fd */ -1, filename_.c_str(), filename_.c_str(), - nullptr, - nullptr, + /* requested_base */ nullptr, executable, - false /* low_4gb */, + /* low_4gb */ false, oat_file_assistant_->dex_location_.c_str(), + /* reservation */ nullptr, &error_msg)); } if (file_.get() == nullptr) { diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index 12dfe20d56..51d8fca6c5 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -77,11 +77,11 @@ TEST_F(OatFileTest, LoadOat) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr); @@ -105,11 +105,11 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) { std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, oat_location.c_str(), oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); ASSERT_TRUE(odex_file != nullptr); ASSERT_EQ(2u, odex_file->GetOatDexFiles().size()); @@ -120,13 +120,13 @@ TEST_F(OatFileTest, ChangingMultiDexUncompressed) { // And try to load again. std::unique_ptr<OatFile> odex_file(OatFile::Open(/* zip_fd */ -1, - oat_location.c_str(), - oat_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, + oat_location, + oat_location, + /* requested_base */ nullptr, + /* executable */ false, + /* low_4gb */ false, dex_location.c_str(), + /* reservation */ nullptr, &error_msg)); EXPECT_TRUE(odex_file == nullptr); EXPECT_NE(std::string::npos, error_msg.find("expected 2 uncompressed dex files, but found 1")) diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index a7dc2257f1..452cd8e359 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -150,10 +150,11 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, (writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ, unquicken ? MAP_PRIVATE : MAP_SHARED, file_fd, - 0 /* start offset */, + /* start */ 0u, low_4gb, - mmap_reuse, vdex_filename.c_str(), + mmap_reuse, + /* reservation */ nullptr, error_msg); if (!mmap.IsValid()) { *error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg; diff --git a/test/305-other-fault-handler/fault_handler.cc b/test/305-other-fault-handler/fault_handler.cc index 93bb148745..db3f1f4bf7 100644 --- a/test/305-other-fault-handler/fault_handler.cc +++ b/test/305-other-fault-handler/fault_handler.cc @@ -40,6 +40,7 @@ class TestFaultHandler final : public FaultHandler { /* prot */ PROT_NONE, /* low_4gb */ false, /* reuse */ false, + /* reservation */ nullptr, /* error_msg */ &map_error_, /* use_ashmem */ false)), was_hit_(false) { |