diff options
author | 2018-09-27 16:42:44 +0000 | |
---|---|---|
committer | 2018-09-28 09:12:06 +0100 | |
commit | 4df2d8041f5dcc7af8c3b3b60b0ea87a1e0d3b94 (patch) | |
tree | 0273072a2b65d6c0cf692a3e7f8eab9814d9d23d | |
parent | 233b572a940431a94a1790750afdceab2d6f4fde (diff) |
Revert^2 "Load boot image at a random address."
This reverts commit f3d077373536c54824e4449759dff2f18369eab3.
Fixed Heap constructor to reserve extra space for GSS.
Change-Id: I6a65be35f4aa183304db5491da4a4810d8e3b266
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --relocate --no-relocate
Test: Pixel 2 XL boots.
Test: m test-art-target-gtest
Test: testrunner.py --target --optimizing --relocate --no-relocate
Test: art/test/testrunner/run_build_test_target.py -j48 art-gtest-gss-gc-tlab
Bug: 77856493
27 files changed, 784 insertions, 563 deletions
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h index b7117bd223..e92b67a0e8 100644 --- a/compiler/dex/quick_compiler_callbacks.h +++ b/compiler/dex/quick_compiler_callbacks.h @@ -38,11 +38,6 @@ class QuickCompilerCallbacks final : public CompilerCallbacks { void ClassRejected(ClassReference ref) override; - // We are running in an environment where we can call patchoat safely so we should. - bool IsRelocationPossible() override { - return true; - } - verifier::VerifierDeps* GetVerifierDeps() const override { return verifier_deps_.get(); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 306b73f86b..e1b23cc315 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -49,7 +49,6 @@ class VerifierDepsCompilerCallbacks : public CompilerCallbacks { void MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) override {} void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) override {} - bool IsRelocationPossible() override { return false; } verifier::VerifierDeps* GetVerifierDeps() const override { return deps_; } void SetVerifierDeps(verifier::VerifierDeps* deps) override { deps_ = deps; } diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 1b2e8d7526..245a15b236 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -36,7 +36,7 @@ #include "class_linker.h" #include "gc/heap.h" #include "gc/space/image_space.h" -#include "image.h" +#include "image-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "oat.h" diff --git a/runtime/art_method.h b/runtime/art_method.h index 48ddc6992d..570b0e6ef9 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -725,6 +725,10 @@ class ArtMethod final { ALWAYS_INLINE CodeItemDebugInfoAccessor DexInstructionDebugInfo() REQUIRES_SHARED(Locks::mutator_lock_); + GcRoot<mirror::Class>& DeclaringClassRoot() { + return declaring_class_; + } + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h index 6855dcdb2b..b29eb7050d 100644 --- a/runtime/compiler_callbacks.h +++ b/runtime/compiler_callbacks.h @@ -51,10 +51,6 @@ class CompilerCallbacks { REQUIRES_SHARED(Locks::mutator_lock_) = 0; virtual void ClassRejected(ClassReference ref) = 0; - // Return true if we should attempt to relocate to a random base address if we have not already - // done so. Return false if relocating in this way would be problematic. - virtual bool IsRelocationPossible() = 0; - virtual verifier::VerifierDeps* GetVerifierDeps() const = 0; virtual void SetVerifierDeps(verifier::VerifierDeps* deps ATTRIBUTE_UNUSED) {} diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 19098d5dbb..78e8422887 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -336,9 +336,13 @@ Heap::Heap(size_t initial_size, // Requested begin for the alloc space, to follow the mapped image and oat files uint8_t* request_begin = nullptr; // Calculate the extra space required after the boot image, see allocations below. - size_t heap_reservation_size = separate_non_moving_space - ? non_moving_space_capacity - : ((is_zygote && foreground_collector_type_ != kCollectorTypeCC) ? capacity_ : 0u); + size_t heap_reservation_size = 0u; + if (separate_non_moving_space) { + heap_reservation_size = non_moving_space_capacity; + } else if ((foreground_collector_type_ != kCollectorTypeCC) && + (is_zygote || foreground_collector_type_ == kCollectorTypeGSS)) { + heap_reservation_size = capacity_; + } heap_reservation_size = RoundUp(heap_reservation_size, kPageSize); // Load image space(s). std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces; @@ -415,13 +419,14 @@ Heap::Heap(size_t initial_size, // Attempt to create 2 mem maps at or after the requested begin. if (foreground_collector_type_ != kCollectorTypeCC) { ScopedTrace trace2("Create main mem map"); - if (separate_non_moving_space || !is_zygote) { + if (separate_non_moving_space || + !(is_zygote || foreground_collector_type_ == kCollectorTypeGSS)) { main_mem_map_1 = MapAnonymousPreferredAddress( kMemMapSpaceName[0], request_begin, capacity_, &error_str); } else { - // If no separate non-moving space and we are the zygote, the main space must come right - // after the image space to avoid a gap. This is required since we want the zygote space to - // be adjacent to the image space. + // If no separate non-moving space and we are the zygote or the collector type is GSS, + // the main space must come right after the image space to avoid a gap. + // This is required since we want the zygote space to be adjacent to the image space. DCHECK_EQ(heap_reservation.IsValid(), !boot_image_spaces_.empty()); main_mem_map_1 = MemMap::MapAnonymous( kMemMapSpaceName[0], diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index ee4a0f4f56..8af5d559eb 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -28,6 +28,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/bit_memory_region.h" #include "base/callee_save_type.h" #include "base/enums.h" #include "base/file_utils.h" @@ -38,13 +39,16 @@ #include "base/systrace.h" #include "base/time_utils.h" #include "base/utils.h" +#include "class_root.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/accounting/space_bitmap-inl.h" #include "image-inl.h" #include "image_space_fs.h" +#include "intern_table.h" #include "mirror/class-inl.h" +#include "mirror/executable.h" #include "mirror/object-inl.h" #include "mirror/object-refvisitor-inl.h" #include "oat_file.h" @@ -55,7 +59,6 @@ namespace art { namespace gc { namespace space { -using android::base::StringAppendF; using android::base::StringPrintf; Atomic<uint32_t> ImageSpace::bitmap_index_(0); @@ -239,142 +242,37 @@ static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_hea return true; } -// Relocate the image at image_location to dest_filename and relocate it by a random amount. -static bool RelocateImage(const char* image_location, - const char* dest_directory, - InstructionSet isa, - std::string* error_msg) { - // We should clean up so we are more likely to have room for the image. - if (Runtime::Current()->IsZygote()) { - LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile"; - PruneDalvikCache(isa); - } - - std::string patchoat(Runtime::Current()->GetPatchoatExecutable()); - - std::string input_image_location_arg("--input-image-location="); - input_image_location_arg += image_location; - - std::string output_image_directory_arg("--output-image-directory="); - output_image_directory_arg += dest_directory; - - std::string instruction_set_arg("--instruction-set="); - instruction_set_arg += GetInstructionSetString(isa); - - std::string base_offset_arg("--base-offset-delta="); - StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta()); - - std::vector<std::string> argv; - argv.push_back(patchoat); - - argv.push_back(input_image_location_arg); - argv.push_back(output_image_directory_arg); - - argv.push_back(instruction_set_arg); - argv.push_back(base_offset_arg); - - std::string command_line(android::base::Join(argv, ' ')); - LOG(INFO) << "RelocateImage: " << command_line; - return Exec(argv, error_msg); -} - -static bool VerifyImage(const char* image_location, - const char* dest_directory, - InstructionSet isa, - std::string* error_msg) { - std::string patchoat(Runtime::Current()->GetPatchoatExecutable()); - - std::string input_image_location_arg("--input-image-location="); - input_image_location_arg += image_location; - - std::string output_image_directory_arg("--output-image-directory="); - output_image_directory_arg += dest_directory; - - std::string instruction_set_arg("--instruction-set="); - instruction_set_arg += GetInstructionSetString(isa); - - std::vector<std::string> argv; - argv.push_back(patchoat); - - argv.push_back(input_image_location_arg); - argv.push_back(output_image_directory_arg); - - argv.push_back(instruction_set_arg); - - argv.push_back("--verify"); - - std::string command_line(android::base::Join(argv, ' ')); - LOG(INFO) << "VerifyImage: " << command_line; - return Exec(argv, error_msg); -} - -static ImageHeader* ReadSpecificImageHeader(const char* filename, std::string* error_msg) { +static std::unique_ptr<ImageHeader> ReadSpecificImageHeader(const char* filename, + std::string* error_msg) { std::unique_ptr<ImageHeader> hdr(new ImageHeader); if (!ReadSpecificImageHeader(filename, hdr.get())) { *error_msg = StringPrintf("Unable to read image header for %s", filename); return nullptr; } - return hdr.release(); + return hdr; } -ImageHeader* ImageSpace::ReadImageHeader(const char* image_location, - const InstructionSet image_isa, - std::string* error_msg) { +std::unique_ptr<ImageHeader> ImageSpace::ReadImageHeader(const char* image_location, + const InstructionSet image_isa, + std::string* error_msg) { std::string system_filename; bool has_system = false; std::string cache_filename; bool has_cache = false; bool dalvik_cache_exists = false; bool is_global_cache = false; - if (FindImageFilename(image_location, image_isa, &system_filename, &has_system, - &cache_filename, &dalvik_cache_exists, &has_cache, &is_global_cache)) { - if (Runtime::Current()->ShouldRelocate()) { - if (has_system && has_cache) { - std::unique_ptr<ImageHeader> sys_hdr(new ImageHeader); - std::unique_ptr<ImageHeader> cache_hdr(new ImageHeader); - if (!ReadSpecificImageHeader(system_filename.c_str(), sys_hdr.get())) { - *error_msg = StringPrintf("Unable to read image header for %s at %s", - image_location, system_filename.c_str()); - return nullptr; - } - if (!ReadSpecificImageHeader(cache_filename.c_str(), cache_hdr.get())) { - *error_msg = StringPrintf("Unable to read image header for %s at %s", - image_location, cache_filename.c_str()); - return nullptr; - } - if (sys_hdr->GetOatChecksum() != cache_hdr->GetOatChecksum()) { - *error_msg = StringPrintf("Unable to find a relocated version of image file %s", - image_location); - return nullptr; - } - return cache_hdr.release(); - } else if (!has_cache) { - *error_msg = StringPrintf("Unable to find a relocated version of image file %s", - image_location); - return nullptr; - } else if (!has_system && has_cache) { - // This can probably just use the cache one. - return ReadSpecificImageHeader(cache_filename.c_str(), error_msg); - } - } else { - // We don't want to relocate, Just pick the appropriate one if we have it and return. - if (has_system && has_cache) { - // We want the cache if the checksum matches, otherwise the system. - std::unique_ptr<ImageHeader> system(ReadSpecificImageHeader(system_filename.c_str(), - error_msg)); - std::unique_ptr<ImageHeader> cache(ReadSpecificImageHeader(cache_filename.c_str(), - error_msg)); - if (system.get() == nullptr || - (cache.get() != nullptr && cache->GetOatChecksum() == system->GetOatChecksum())) { - return cache.release(); - } else { - return system.release(); - } - } else if (has_system) { - return ReadSpecificImageHeader(system_filename.c_str(), error_msg); - } else if (has_cache) { - return ReadSpecificImageHeader(cache_filename.c_str(), error_msg); - } + if (FindImageFilename(image_location, + image_isa, + &system_filename, + &has_system, + &cache_filename, + &dalvik_cache_exists, + &has_cache, + &is_global_cache)) { + if (has_system) { + return ReadSpecificImageHeader(system_filename.c_str(), error_msg); + } else if (has_cache) { + return ReadSpecificImageHeader(cache_filename.c_str(), error_msg); } } @@ -483,10 +381,66 @@ std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) { // nested class), but not declare functions in the header. class ImageSpace::Loader { public: + static std::unique_ptr<ImageSpace> InitAppImage(const char* image_filename, + const char* image_location, + bool validate_oat_file, + const OatFile* oat_file, + /*inout*/MemMap* image_reservation, + /*inout*/MemMap* oat_reservation, + /*out*/std::string* error_msg) + REQUIRES_SHARED(Locks::mutator_lock_) { + TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image)); + std::unique_ptr<ImageSpace> space = Init(image_filename, + image_location, + validate_oat_file, + oat_file, + &logger, + image_reservation, + oat_reservation, + error_msg); + if (space != nullptr) { + TimingLogger::ScopedTiming timing("RelocateImage", &logger); + ImageHeader* image_header = reinterpret_cast<ImageHeader*>(space->GetMemMap()->Begin()); + if (!RelocateInPlace(*image_header, + space->GetMemMap()->Begin(), + space->GetLiveBitmap(), + oat_file, + error_msg)) { + return nullptr; + } + Runtime* runtime = Runtime::Current(); + CHECK_EQ(runtime->GetResolutionMethod(), + image_header->GetImageMethod(ImageHeader::kResolutionMethod)); + CHECK_EQ(runtime->GetImtConflictMethod(), + image_header->GetImageMethod(ImageHeader::kImtConflictMethod)); + CHECK_EQ(runtime->GetImtUnimplementedMethod(), + image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves), + image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly), + image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs), + image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything), + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit), + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck), + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck)); + + VLOG(image) << "ImageSpace::Loader::InitAppImage exiting " << *space.get(); + if (VLOG_IS_ON(image)) { + logger.Dump(LOG_STREAM(INFO)); + } + } + return space; + } + static std::unique_ptr<ImageSpace> Init(const char* image_filename, const char* image_location, bool validate_oat_file, const OatFile* oat_file, + TimingLogger* logger, /*inout*/MemMap* image_reservation, /*inout*/MemMap* oat_reservation, /*out*/std::string* error_msg) @@ -494,12 +448,11 @@ class ImageSpace::Loader { CHECK(image_filename != nullptr); CHECK(image_location != nullptr); - TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image)); VLOG(image) << "ImageSpace::Init entering image_filename=" << image_filename; std::unique_ptr<File> file; { - TimingLogger::ScopedTiming timing("OpenImageFile", &logger); + TimingLogger::ScopedTiming timing("OpenImageFile", logger); file.reset(OS::OpenFileForReading(image_filename)); if (file == nullptr) { *error_msg = StringPrintf("Failed to open '%s'", image_filename); @@ -509,7 +462,7 @@ class ImageSpace::Loader { ImageHeader temp_image_header; ImageHeader* image_header = &temp_image_header; { - TimingLogger::ScopedTiming timing("ReadImageHeader", &logger); + TimingLogger::ScopedTiming timing("ReadImageHeader", logger); bool success = file->ReadFully(image_header, sizeof(*image_header)); if (!success || !image_header->IsValid()) { *error_msg = StringPrintf("Invalid image header in '%s'", image_filename); @@ -586,24 +539,10 @@ class ImageSpace::Loader { 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_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? + error_msg); if (!map.IsValid()) { DCHECK(!error_msg->empty()); return nullptr; @@ -611,7 +550,8 @@ class ImageSpace::Loader { DCHECK_EQ(0, memcmp(image_header, map.Begin(), sizeof(ImageHeader))); MemMap image_bitmap_map = MemMap::MapFile(bitmap_section.Size(), - PROT_READ, MAP_PRIVATE, + PROT_READ, + MAP_PRIVATE, file->Fd(), image_bitmap_offset, /* low_4gb */ false, @@ -634,7 +574,7 @@ class ImageSpace::Loader { uint8_t* const image_end = map.Begin() + image_objects.End(); std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap; { - TimingLogger::ScopedTiming timing("CreateImageBitmap", &logger); + TimingLogger::ScopedTiming timing("CreateImageBitmap", logger); bitmap.reset( accounting::ContinuousSpaceBitmap::CreateFromMemMap( bitmap_name, @@ -647,16 +587,6 @@ class ImageSpace::Loader { return nullptr; } } - { - TimingLogger::ScopedTiming timing("RelocateImage", &logger); - if (!RelocateInPlace(*image_header, - map.Begin(), - bitmap.get(), - oat_file, - error_msg)) { - return nullptr; - } - } // We only want the mirror object, not the ArtFields and ArtMethods. std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location, @@ -670,7 +600,7 @@ class ImageSpace::Loader { // Object::SizeOf() which VerifyImageAllocations() calls, are not // set yet at this point. if (oat_file == nullptr) { - TimingLogger::ScopedTiming timing("OpenOatFile", &logger); + TimingLogger::ScopedTiming timing("OpenOatFile", logger); space->oat_file_ = OpenOatFile(*space, image_filename, oat_reservation, error_msg); if (space->oat_file_ == nullptr) { DCHECK(!error_msg->empty()); @@ -682,7 +612,7 @@ class ImageSpace::Loader { } if (validate_oat_file) { - TimingLogger::ScopedTiming timing("ValidateOatFile", &logger); + TimingLogger::ScopedTiming timing("ValidateOatFile", logger); CHECK(space->oat_file_ != nullptr); if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) { DCHECK(!error_msg->empty()); @@ -690,60 +620,6 @@ class ImageSpace::Loader { } } - Runtime* runtime = Runtime::Current(); - - // If oat_file is null, then it is the boot image space. Use oat_file_non_owned_ from the space - // to set the runtime methods. - CHECK_EQ(oat_file != nullptr, image_header->IsAppImage()); - if (image_header->IsAppImage()) { - CHECK_EQ(runtime->GetResolutionMethod(), - image_header->GetImageMethod(ImageHeader::kResolutionMethod)); - CHECK_EQ(runtime->GetImtConflictMethod(), - image_header->GetImageMethod(ImageHeader::kImtConflictMethod)); - CHECK_EQ(runtime->GetImtUnimplementedMethod(), - image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves), - image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly), - image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs), - image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything), - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit), - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit)); - CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck), - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck)); - } else if (!runtime->HasResolutionMethod()) { - runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet()); - runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod)); - runtime->SetImtConflictMethod(image_header->GetImageMethod(ImageHeader::kImtConflictMethod)); - runtime->SetImtUnimplementedMethod( - image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod)); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod), - CalleeSaveType::kSaveAllCalleeSaves); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveRefsOnlyMethod), - CalleeSaveType::kSaveRefsOnly); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod), - CalleeSaveType::kSaveRefsAndArgs); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod), - CalleeSaveType::kSaveEverything); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit), - CalleeSaveType::kSaveEverythingForClinit); - runtime->SetCalleeSaveMethod( - image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck), - CalleeSaveType::kSaveEverythingForSuspendCheck); - } - - VLOG(image) << "ImageSpace::Init exiting " << *space.get(); - if (VLOG_IS_ON(image)) { - logger.Dump(LOG_STREAM(INFO)); - } return space; } @@ -751,12 +627,12 @@ class ImageSpace::Loader { static MemMap LoadImageFile(const char* image_filename, const char* image_location, const ImageHeader& image_header, - uint8_t* address, int fd, - TimingLogger& logger, + TimingLogger* logger, /*inout*/MemMap* image_reservation, /*out*/std::string* error_msg) { - TimingLogger::ScopedTiming timing("MapImageFile", &logger); + TimingLogger::ScopedTiming timing("MapImageFile", logger); + uint8_t* address = (image_reservation != nullptr) ? image_reservation->Begin() : nullptr; const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode(); if (storage_mode == ImageHeader::kStorageModeUncompressed) { return MemMap::MapFileAtAddress(address, @@ -764,10 +640,10 @@ class ImageSpace::Loader { PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, - /* start */ 0, - /* low_4gb */ true, + /* start= */ 0, + /* low_4gb= */ true, image_filename, - /* reuse */ false, + /* reuse= */ false, image_reservation, error_msg); } @@ -786,8 +662,8 @@ class ImageSpace::Loader { address, image_header.GetImageSize(), PROT_READ | PROT_WRITE, - /* low_4gb */ true, - /* reuse */ false, + /* low_4gb= */ true, + /* reuse= */ false, image_reservation, error_msg); if (map.IsValid()) { @@ -797,8 +673,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()) { @@ -808,7 +684,7 @@ class ImageSpace::Loader { memcpy(map.Begin(), &image_header, sizeof(ImageHeader)); const uint64_t start = NanoTime(); // LZ4HC and LZ4 have same internal format, both use LZ4_decompress. - TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); + TimingLogger::ScopedTiming timing2("LZ4 decompress image", logger); const size_t decompressed_size = LZ4_decompress_safe( reinterpret_cast<char*>(temp_map.Begin()) + sizeof(ImageHeader), reinterpret_cast<char*>(map.Begin()) + decompress_offset, @@ -1136,23 +1012,14 @@ class ImageSpace::Loader { }; // Relocate an image space mapped at target_base which possibly used to be at a different base - // address. Only needs a single image space, not one for both source and destination. - // In place means modifying a single ImageSpace in place rather than relocating from one ImageSpace - // to another. + // address. In place means modifying a single ImageSpace in place rather than relocating from + // one ImageSpace to another. static bool RelocateInPlace(ImageHeader& image_header, uint8_t* target_base, accounting::ContinuousSpaceBitmap* bitmap, const OatFile* app_oat_file, std::string* error_msg) { DCHECK(error_msg != nullptr); - if (!image_header.IsPic()) { - if (image_header.GetImageBegin() == target_base) { - return true; - } - *error_msg = StringPrintf("Cannot relocate non-pic image for oat file %s", - (app_oat_file != nullptr) ? app_oat_file->GetLocation().c_str() : ""); - return false; - } // Set up sections. uint32_t boot_image_begin = 0; uint32_t boot_image_end = 0; @@ -1372,13 +1239,17 @@ class ImageSpace::Loader { CHECK(image_header.GetOatDataBegin() != nullptr); - std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, + uint8_t* oat_data_begin = image_header.GetOatDataBegin(); + if (oat_reservation != nullptr) { + oat_data_begin += oat_reservation->Begin() - image_header.GetOatFileBegin(); + } + std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd= */ -1, oat_filename, oat_filename, - image_header.GetOatDataBegin(), + oat_data_begin, !Runtime::Current()->IsAotCompiler(), - /* low_4gb */ false, - /* abs_dex_location */ nullptr, + /* low_4gb= */ false, + /* abs_dex_location= */ nullptr, oat_reservation, error_msg)); if (oat_file == nullptr) { @@ -1452,6 +1323,7 @@ class ImageSpace::BootImageLoader { /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces, /*out*/MemMap* extra_reservation, /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { + TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image)); std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_); std::vector<std::string> locations; if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) { @@ -1490,7 +1362,8 @@ class ImageSpace::BootImageLoader { filename = GetSystemImageFilename(location.c_str(), image_isa_); spaces.push_back(Load(location, filename, - /* validate_oat_file */ false, + /* validate_oat_file= */ false, + &logger, &image_reservation, &oat_reservation, error_msg)); @@ -1502,18 +1375,25 @@ class ImageSpace::BootImageLoader { return false; } + MaybeRelocateSpaces(spaces, &logger); + InitRuntimeMethods(spaces); *extra_reservation = std::move(local_extra_reservation); boot_image_spaces->swap(spaces); + + VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front(); + if (VLOG_IS_ON(image)) { + logger.Dump(LOG_STREAM(INFO)); + } return true; } bool LoadFromDalvikCache( - bool validate_system_checksums, bool validate_oat_file, size_t extra_reservation_size, /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces, /*out*/MemMap* extra_reservation, /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) { + TimingLogger logger(__PRETTY_FUNCTION__, true, VLOG_IS_ON(image)); DCHECK(DalvikCacheExists()); std::vector<std::string> locations; if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) { @@ -1562,42 +1442,531 @@ class ImageSpace::BootImageLoader { spaces.push_back(Load(location, filename, validate_oat_file, + &logger, &image_reservation, &oat_reservation, error_msg)); if (spaces.back() == nullptr) { return false; } - if (validate_system_checksums) { - ImageHeader system_hdr; - std::string system_filename = GetSystemImageFilename(location.c_str(), image_isa_); - if (!ReadSpecificImageHeader(system_filename.c_str(), &system_hdr)) { - *error_msg = StringPrintf("Cannot read header of %s", system_filename.c_str()); - return false; - } - if (spaces.back()->GetImageHeader().GetOatChecksum() != system_hdr.GetOatChecksum()) { - *error_msg = StringPrintf("Checksum mismatch: %u(%s) vs %u(%s)", - spaces.back()->GetImageHeader().GetOatChecksum(), - filename.c_str(), - system_hdr.GetOatChecksum(), - system_filename.c_str()); - return false; - } - } } if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) { return false; } + MaybeRelocateSpaces(spaces, &logger); + InitRuntimeMethods(spaces); *extra_reservation = std::move(local_extra_reservation); boot_image_spaces->swap(spaces); + + VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front(); + if (VLOG_IS_ON(image)) { + logger.Dump(LOG_STREAM(INFO)); + } return true; } private: + template <typename T> + ALWAYS_INLINE static T* RelocatedAddress(T* src, uint32_t diff) { + DCHECK(src != nullptr); + return reinterpret_cast32<T*>(reinterpret_cast32<uint32_t>(src) + diff); + } + + template <bool kMayBeNull = true, typename T> + ALWAYS_INLINE static void PatchGcRoot(uint32_t diff, /*inout*/GcRoot<T>* root) + REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(sizeof(GcRoot<mirror::Class*>) == sizeof(uint32_t), "GcRoot size check"); + T* old_value = root->template Read<kWithoutReadBarrier>(); + DCHECK(kMayBeNull || old_value != nullptr); + if (!kMayBeNull || old_value != nullptr) { + *root = GcRoot<T>(RelocatedAddress(old_value, diff)); + } + } + + template <PointerSize kPointerSize, bool kMayBeNull = true, typename T> + ALWAYS_INLINE static void PatchNativePointer(uint32_t diff, /*inout*/T** entry) { + if (kPointerSize == PointerSize::k64) { + uint64_t* raw_entry = reinterpret_cast<uint64_t*>(entry); + T* old_value = reinterpret_cast64<T*>(*raw_entry); + DCHECK(kMayBeNull || old_value != nullptr); + if (!kMayBeNull || old_value != nullptr) { + T* new_value = RelocatedAddress(old_value, diff); + *raw_entry = reinterpret_cast64<uint64_t>(new_value); + } + } else { + uint32_t* raw_entry = reinterpret_cast<uint32_t*>(entry); + T* old_value = reinterpret_cast32<T*>(*raw_entry); + DCHECK(kMayBeNull || old_value != nullptr); + if (!kMayBeNull || old_value != nullptr) { + T* new_value = RelocatedAddress(old_value, diff); + *raw_entry = reinterpret_cast32<uint32_t>(new_value); + } + } + } + + class PatchedObjectsMap { + public: + PatchedObjectsMap(uint8_t* image_space_begin, size_t size) + : image_space_begin_(image_space_begin), + data_(new uint8_t[BitsToBytesRoundUp(NumLocations(size))]), + visited_objects_(data_.get(), /* bit_start= */ 0u, NumLocations(size)) { + DCHECK_ALIGNED(image_space_begin_, kObjectAlignment); + std::memset(data_.get(), 0, BitsToBytesRoundUp(NumLocations(size))); + } + + ALWAYS_INLINE bool IsVisited(mirror::Object* object) const { + return visited_objects_.LoadBit(GetIndex(object)); + } + + ALWAYS_INLINE void MarkVisited(mirror::Object* object) { + DCHECK(!IsVisited(object)); + visited_objects_.StoreBit(GetIndex(object), /* value= */ true); + } + + private: + static size_t NumLocations(size_t size) { + DCHECK_ALIGNED(size, kObjectAlignment); + return size / kObjectAlignment; + } + + size_t GetIndex(mirror::Object* object) const { + DCHECK_ALIGNED(object, kObjectAlignment); + return (reinterpret_cast<uint8_t*>(object) - image_space_begin_) / kObjectAlignment; + } + + uint8_t* const image_space_begin_; + const std::unique_ptr<uint8_t[]> data_; + BitMemoryRegion visited_objects_; + }; + + class PatchArtFieldVisitor final : public ArtFieldVisitor { + public: + explicit PatchArtFieldVisitor(uint32_t diff) + : diff_(diff) {} + + void Visit(ArtField* field) override REQUIRES_SHARED(Locks::mutator_lock_) { + PatchGcRoot</* kMayBeNull */ false>(diff_, &field->DeclaringClassRoot()); + } + + private: + const uint32_t diff_; + }; + + template <PointerSize kPointerSize> + class PatchArtMethodVisitor final : public ArtMethodVisitor { + public: + explicit PatchArtMethodVisitor(uint32_t diff) + : diff_(diff) {} + + void Visit(ArtMethod* method) override REQUIRES_SHARED(Locks::mutator_lock_) { + PatchGcRoot(diff_, &method->DeclaringClassRoot()); + void** data_address = PointerAddress(method, ArtMethod::DataOffset(kPointerSize)); + PatchNativePointer<kPointerSize>(diff_, data_address); + void** entrypoint_address = + PointerAddress(method, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kPointerSize)); + PatchNativePointer<kPointerSize>(diff_, entrypoint_address); + } + + private: + void** PointerAddress(ArtMethod* method, MemberOffset offset) { + return reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(method) + offset.Uint32Value()); + } + + const uint32_t diff_; + }; + + class ClassTableVisitor final { + public: + explicit ClassTableVisitor(uint32_t diff) : diff_(diff) {} + + void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(root->AsMirrorPtr() != nullptr); + root->Assign(RelocatedAddress(root->AsMirrorPtr(), diff_)); + } + + private: + const uint32_t diff_; + }; + + template <PointerSize kPointerSize> + class PatchObjectVisitor final { + public: + explicit PatchObjectVisitor(uint32_t diff) + : diff_(diff) {} + + void VisitClass(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_) { + // First, patch the `klass->klass_`, known to be a reference to the j.l.Class.class. + // This should be the only reference field in j.l.Object and we assert that below. + PatchReferenceField</* kMayBeNull */ false>(klass, mirror::Object::ClassOffset()); + // Then patch the reference instance fields described by j.l.Class.class. + // Use the sizeof(Object) to determine where these reference fields start. + mirror::Class* class_class = klass->GetClass<kVerifyNone, kWithoutReadBarrier>(); + size_t num_reference_instance_fields = class_class->NumReferenceInstanceFields<kVerifyNone>(); + DCHECK_NE(num_reference_instance_fields, 0u); + static_assert(IsAligned<kHeapReferenceSize>(sizeof(mirror::Object)), "Size alignment check."); + MemberOffset instance_field_offset(sizeof(mirror::Object)); + for (size_t i = 0; i != num_reference_instance_fields; ++i) { + PatchReferenceField(klass, instance_field_offset); + instance_field_offset = MemberOffset( + instance_field_offset.Uint32Value() + sizeof(mirror::HeapReference<mirror::Object>)); + } + // Now that we have patched the `super_class_`, if this is the j.l.Class.class, + // we can get a reference to j.l.Object.class and assert that it has only one + // reference instance field (the `klass_` patched above). + if (kIsDebugBuild && klass == class_class) { + mirror::Class* object_class = klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>(); + CHECK_EQ(object_class->NumReferenceInstanceFields<kVerifyNone>(), 1u); + } + // Then patch static fields. + size_t num_reference_static_fields = klass->NumReferenceStaticFields<kVerifyNone>(); + if (num_reference_static_fields != 0u) { + MemberOffset static_field_offset = + klass->GetFirstReferenceStaticFieldOffset<kVerifyNone>(kPointerSize); + for (size_t i = 0; i != num_reference_static_fields; ++i) { + PatchReferenceField(klass, static_field_offset); + static_field_offset = MemberOffset( + static_field_offset.Uint32Value() + sizeof(mirror::HeapReference<mirror::Object>)); + } + } + // Then patch native pointers. + klass->FixupNativePointers<kVerifyNone>(klass, kPointerSize, *this); + } + + template <typename T> + T* operator()(T* ptr, void** dest_addr ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(Locks::mutator_lock_) { + if (ptr != nullptr) { + ptr = RelocatedAddress(ptr, diff_); + } + return ptr; + } + + void VisitPointerArray(mirror::PointerArray* pointer_array) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Fully patch the pointer array, including the `klass_` field. + PatchReferenceField</* kMayBeNull */ false>(pointer_array, mirror::Object::ClassOffset()); + + int32_t length = pointer_array->GetLength<kVerifyNone>(); + for (int32_t i = 0; i != length; ++i) { + ArtMethod** method_entry = reinterpret_cast<ArtMethod**>( + pointer_array->ElementAddress<kVerifyNone>(i, kPointerSize)); + PatchNativePointer<kPointerSize, /* kMayBeNull */ false>(diff_, method_entry); + } + } + + void VisitObject(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + // Visit all reference fields. + object->VisitReferences</* kVisitNativeRoots */ false, + kVerifyNone, + kWithoutReadBarrier>(*this, *this); + // This function should not be called for classes. + DCHECK(!object->IsClass<kVerifyNone>()); + } + + // Visitor for VisitReferences(). + ALWAYS_INLINE void operator()(mirror::Object* object, MemberOffset field_offset, bool is_static) + const REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!is_static); + PatchReferenceField(object, field_offset); + } + // Visitor for VisitReferences(), java.lang.ref.Reference case. + ALWAYS_INLINE void operator()(ObjPtr<mirror::Class> klass, mirror::Reference* ref) const + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(klass->IsTypeOfReferenceClass()); + this->operator()(ref, mirror::Reference::ReferentOffset(), /* is_static= */ false); + } + // Ignore class native roots; not called from VisitReferences() for kVisitNativeRoots == false. + void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) + const {} + void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} + + void VisitDexCacheArrays(mirror::DexCache* dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) { + FixupDexCacheArray<mirror::StringDexCacheType>(dex_cache, + mirror::DexCache::StringsOffset(), + dex_cache->NumStrings<kVerifyNone>()); + FixupDexCacheArray<mirror::TypeDexCacheType>(dex_cache, + mirror::DexCache::ResolvedTypesOffset(), + dex_cache->NumResolvedTypes<kVerifyNone>()); + FixupDexCacheArray<mirror::MethodDexCacheType>(dex_cache, + mirror::DexCache::ResolvedMethodsOffset(), + dex_cache->NumResolvedMethods<kVerifyNone>()); + FixupDexCacheArray<mirror::FieldDexCacheType>(dex_cache, + mirror::DexCache::ResolvedFieldsOffset(), + dex_cache->NumResolvedFields<kVerifyNone>()); + FixupDexCacheArray<mirror::MethodTypeDexCacheType>( + dex_cache, + mirror::DexCache::ResolvedMethodTypesOffset(), + dex_cache->NumResolvedMethodTypes<kVerifyNone>()); + FixupDexCacheArray<GcRoot<mirror::CallSite>>( + dex_cache, + mirror::DexCache::ResolvedCallSitesOffset(), + dex_cache->NumResolvedCallSites<kVerifyNone>()); + } + + private: + template <bool kMayBeNull = true> + ALWAYS_INLINE void PatchReferenceField(mirror::Object* object, MemberOffset offset) const + REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Object* old_value = + object->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset); + DCHECK(kMayBeNull || old_value != nullptr); + if (!kMayBeNull || old_value != nullptr) { + mirror::Object* new_value = RelocatedAddress(old_value, diff_); + object->SetFieldObjectWithoutWriteBarrier</* kTransactionActive */ false, + /* kCheckTransaction */ true, + kVerifyNone>(offset, new_value); + } + } + + template <typename T> + void FixupDexCacheArrayEntry(std::atomic<mirror::DexCachePair<T>>* array, uint32_t index) + REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(sizeof(std::atomic<mirror::DexCachePair<T>>) == sizeof(mirror::DexCachePair<T>), + "Size check for removing std::atomic<>."); + PatchGcRoot(diff_, &(reinterpret_cast<mirror::DexCachePair<T>*>(array)[index].object)); + } + + template <typename T> + void FixupDexCacheArrayEntry(std::atomic<mirror::NativeDexCachePair<T>>* array, uint32_t index) + REQUIRES_SHARED(Locks::mutator_lock_) { + static_assert(sizeof(std::atomic<mirror::NativeDexCachePair<T>>) == + sizeof(mirror::NativeDexCachePair<T>), + "Size check for removing std::atomic<>."); + mirror::NativeDexCachePair<T> pair = + mirror::DexCache::GetNativePairPtrSize(array, index, kPointerSize); + if (pair.object != nullptr) { + pair.object = RelocatedAddress(pair.object, diff_); + mirror::DexCache::SetNativePairPtrSize(array, index, pair, kPointerSize); + } + } + + void FixupDexCacheArrayEntry(GcRoot<mirror::CallSite>* array, uint32_t index) + REQUIRES_SHARED(Locks::mutator_lock_) { + PatchGcRoot(diff_, &array[index]); + } + + template <typename EntryType> + void FixupDexCacheArray(mirror::DexCache* dex_cache, + MemberOffset array_offset, + uint32_t size) REQUIRES_SHARED(Locks::mutator_lock_) { + EntryType* old_array = + reinterpret_cast64<EntryType*>(dex_cache->GetField64<kVerifyNone>(array_offset)); + DCHECK_EQ(old_array != nullptr, size != 0u); + if (old_array != nullptr) { + EntryType* new_array = RelocatedAddress(old_array, diff_); + dex_cache->SetField64<kVerifyNone>(array_offset, reinterpret_cast64<uint64_t>(new_array)); + for (uint32_t i = 0; i != size; ++i) { + FixupDexCacheArrayEntry(new_array, i); + } + } + } + + const uint32_t diff_; + }; + + template <PointerSize kPointerSize> + static void DoRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces, + uint32_t diff) REQUIRES_SHARED(Locks::mutator_lock_) { + PatchedObjectsMap patched_objects(spaces.front()->Begin(), + spaces.back()->End() - spaces.front()->Begin()); + PatchObjectVisitor<kPointerSize> patch_object_visitor(diff); + + mirror::Class* dcheck_class_class = nullptr; // Used only for a DCHECK(). + for (size_t s = 0, size = spaces.size(); s != size; ++s) { + const ImageSpace* space = spaces[s].get(); + + // First patch the image header. The `diff` is OK for patching 32-bit fields but + // the 64-bit method fields in the ImageHeader may need a negative `delta`. + reinterpret_cast<ImageHeader*>(space->Begin())->RelocateImage( + (reinterpret_cast32<uint32_t>(space->Begin()) < diff) + ? -static_cast<int64_t>(-diff) : static_cast<int64_t>(diff)); + + // Patch fields and methods. + const ImageHeader& image_header = space->GetImageHeader(); + PatchArtFieldVisitor field_visitor(diff); + image_header.VisitPackedArtFields(&field_visitor, space->Begin()); + PatchArtMethodVisitor<kPointerSize> method_visitor(diff); + image_header.VisitPackedArtMethods(&method_visitor, space->Begin(), kPointerSize); + auto method_table_visitor = [diff](ArtMethod* method) { + DCHECK(method != nullptr); + return RelocatedAddress(method, diff); + }; + image_header.VisitPackedImTables(method_table_visitor, space->Begin(), kPointerSize); + image_header.VisitPackedImtConflictTables(method_table_visitor, space->Begin(), kPointerSize); + + // Patch the intern table. + if (image_header.GetInternedStringsSection().Size() != 0u) { + const uint8_t* data = space->Begin() + image_header.GetInternedStringsSection().Offset(); + size_t read_count; + InternTable::UnorderedSet temp_set(data, /* make_copy_of_data= */ false, &read_count); + for (GcRoot<mirror::String>& slot : temp_set) { + PatchGcRoot</* kMayBeNull */ false>(diff, &slot); + } + } + + // Patch the class table and classes, so that we can traverse class hierarchy to + // determine the types of other objects when we visit them later. + if (image_header.GetClassTableSection().Size() != 0u) { + uint8_t* data = space->Begin() + image_header.GetClassTableSection().Offset(); + size_t read_count; + ClassTable::ClassSet temp_set(data, /* make_copy_of_data= */ false, &read_count); + DCHECK(!temp_set.empty()); + ClassTableVisitor class_table_visitor(diff); + for (ClassTable::TableSlot& slot : temp_set) { + slot.VisitRoot(class_table_visitor); + mirror::Class* klass = slot.Read<kWithoutReadBarrier>(); + DCHECK(klass != nullptr); + patched_objects.MarkVisited(klass); + patch_object_visitor.VisitClass(klass); + if (kIsDebugBuild) { + mirror::Class* class_class = klass->GetClass<kVerifyNone, kWithoutReadBarrier>(); + if (dcheck_class_class == nullptr) { + dcheck_class_class = class_class; + } else { + CHECK_EQ(class_class, dcheck_class_class); + } + } + // Then patch the non-embedded vtable and iftable. + mirror::PointerArray* vtable = klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(); + if (vtable != nullptr && !patched_objects.IsVisited(vtable)) { + patched_objects.MarkVisited(vtable); + patch_object_visitor.VisitPointerArray(vtable); + } + auto* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); + if (iftable != nullptr) { + int32_t ifcount = klass->GetIfTableCount<kVerifyNone, kWithoutReadBarrier>(); + for (int32_t i = 0; i != ifcount; ++i) { + mirror::PointerArray* unpatched_ifarray = + iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i); + if (unpatched_ifarray != nullptr) { + // The iftable has not been patched, so we need to explicitly adjust the pointer. + mirror::PointerArray* ifarray = RelocatedAddress(unpatched_ifarray, diff); + if (!patched_objects.IsVisited(ifarray)) { + patched_objects.MarkVisited(ifarray); + patch_object_visitor.VisitPointerArray(ifarray); + } + } + } + } + } + } + } + + // Patch class roots now, so that we can recognize mirror::Method and mirror::Constructor. + ObjPtr<mirror::Class> method_class; + ObjPtr<mirror::Class> constructor_class; + { + const ImageSpace* space = spaces.front().get(); + const ImageHeader& image_header = space->GetImageHeader(); + + ObjPtr<mirror::ObjectArray<mirror::Object>> image_roots = + image_header.GetImageRoots<kWithoutReadBarrier>(); + patched_objects.MarkVisited(image_roots.Ptr()); + patch_object_visitor.VisitObject(image_roots.Ptr()); + + ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = + ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(MakeObjPtr( + image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kClassRoots))); + patched_objects.MarkVisited(class_roots.Ptr()); + patch_object_visitor.VisitObject(class_roots.Ptr()); + + method_class = GetClassRoot<mirror::Method, kWithoutReadBarrier>(class_roots); + constructor_class = GetClassRoot<mirror::Constructor, kWithoutReadBarrier>(class_roots); + } + + for (size_t s = 0, size = spaces.size(); s != size; ++s) { + const ImageSpace* space = spaces[s].get(); + const ImageHeader& image_header = space->GetImageHeader(); + + static_assert(IsAligned<kObjectAlignment>(sizeof(ImageHeader)), "Header alignment check"); + uint32_t objects_end = image_header.GetObjectsSection().Size(); + DCHECK_ALIGNED(objects_end, kObjectAlignment); + for (uint32_t pos = sizeof(ImageHeader); pos != objects_end; ) { + mirror::Object* object = reinterpret_cast<mirror::Object*>(space->Begin() + pos); + if (!patched_objects.IsVisited(object)) { + // This is the last pass over objects, so we do not need to MarkVisited(). + patch_object_visitor.VisitObject(object); + mirror::Class* klass = object->GetClass<kVerifyNone, kWithoutReadBarrier>(); + if (klass->IsDexCacheClass<kVerifyNone>()) { + // Patch dex cache array pointers and elements. + mirror::DexCache* dex_cache = object->AsDexCache<kVerifyNone, kWithoutReadBarrier>(); + patch_object_visitor.VisitDexCacheArrays(dex_cache); + } else if (klass == method_class || klass == constructor_class) { + // Patch the ArtMethod* in the mirror::Executable subobject. + ObjPtr<mirror::Executable> as_executable = + ObjPtr<mirror::Executable>::DownCast(MakeObjPtr(object)); + ArtMethod* unpatched_method = as_executable->GetArtMethod<kVerifyNone>(); + ArtMethod* patched_method = RelocatedAddress(unpatched_method, diff); + as_executable->SetArtMethod</* kTransactionActive */ false, + /* kCheckTransaction */ true, + kVerifyNone>(patched_method); + } + } + pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment); + } + } + } + + static void MaybeRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces, + TimingLogger* logger) + REQUIRES_SHARED(Locks::mutator_lock_) { + TimingLogger::ScopedTiming timing("MaybeRelocateSpaces", logger); + ImageSpace* first_space = spaces.front().get(); + const ImageHeader& first_space_header = first_space->GetImageHeader(); + uint32_t diff = + static_cast<uint32_t>(first_space->Begin() - first_space_header.GetImageBegin()); + if (!Runtime::Current()->ShouldRelocate()) { + DCHECK_EQ(diff, 0u); + return; + } + + PointerSize pointer_size = first_space_header.GetPointerSize(); + if (pointer_size == PointerSize::k64) { + DoRelocateSpaces<PointerSize::k64>(spaces, diff); + } else { + DoRelocateSpaces<PointerSize::k32>(spaces, diff); + } + } + + static void InitRuntimeMethods(const std::vector<std::unique_ptr<ImageSpace>>& spaces) + REQUIRES_SHARED(Locks::mutator_lock_) { + Runtime* runtime = Runtime::Current(); + DCHECK(!runtime->HasResolutionMethod()); + DCHECK(!spaces.empty()); + ImageSpace* space = spaces[0].get(); + const ImageHeader& image_header = space->GetImageHeader(); + // Use oat_file_non_owned_ from the `space` to set the runtime methods. + runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet()); + runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); + runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); + runtime->SetImtUnimplementedMethod( + image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveAllCalleeSavesMethod), + CalleeSaveType::kSaveAllCalleeSaves); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveRefsOnlyMethod), + CalleeSaveType::kSaveRefsOnly); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod), + CalleeSaveType::kSaveRefsAndArgs); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethod), + CalleeSaveType::kSaveEverything); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit), + CalleeSaveType::kSaveEverythingForClinit); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck), + CalleeSaveType::kSaveEverythingForSuspendCheck); + } + std::unique_ptr<ImageSpace> Load(const std::string& image_location, const std::string& image_filename, bool validate_oat_file, + TimingLogger* logger, /*inout*/MemMap* image_reservation, /*inout*/MemMap* oat_reservation, /*out*/std::string* error_msg) @@ -1629,7 +1998,8 @@ class ImageSpace::BootImageLoader { return Loader::Init(image_filename.c_str(), image_location.c_str(), validate_oat_file, - /* oat_file */ nullptr, + /* oat_file= */ nullptr, + logger, image_reservation, oat_reservation, error_msg); @@ -1642,14 +2012,14 @@ class ImageSpace::BootImageLoader { /*out*/ std::vector<std::string>* all_locations, /*out*/ std::string* error_msg) { std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_filename); - std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd */ -1, + std::unique_ptr<OatFile> oat_file(OatFile::Open(/* zip_fd= */ -1, oat_filename, oat_filename, - /* requested_base */ nullptr, - /* executable */ false, - /* low_4gb */ false, - /* abs_dex_location */ nullptr, - /* reservation */ nullptr, + /* requested_base= */ 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", @@ -1695,18 +2065,35 @@ class ImageSpace::BootImageLoader { DCHECK(!image_reservation->IsValid()); size_t total_size = dchecked_integral_cast<size_t>(oat_end - image_start) + extra_reservation_size; + bool relocate = Runtime::Current()->ShouldRelocate(); + // If relocating, choose a random address for ALSR. Since mmap() does not randomize + // on its own, over-allocate and select a sub-region at a random offset. + size_t randomize_size = relocate + ? RoundUp(ART_BASE_ADDRESS_MAX_DELTA - ART_BASE_ADDRESS_MIN_DELTA, kPageSize) + kPageSize + : 0u; *image_reservation = MemMap::MapAnonymous("Boot image reservation", - reinterpret_cast32<uint8_t*>(image_start), - total_size, + relocate ? nullptr : reinterpret_cast32<uint8_t*>(image_start), + total_size + randomize_size, PROT_NONE, - /* low_4gb */ true, - /* reuse */ false, - /* reservation */ nullptr, + /* low_4gb= */ true, + /* reuse= */ false, + /* reservation= */ nullptr, error_msg); if (!image_reservation->IsValid()) { return false; } + if (relocate) { + uint32_t offset = RoundDown(GetRandomNumber<uint32_t>(0u, randomize_size), kPageSize); + if (offset != 0u) { + MemMap unmapped_head = image_reservation->TakeReservedMemory(offset); + // Let the destructor of `unmapped_head` unmap the memory before the chunk we shall use. + } + DCHECK_LE(total_size, image_reservation->Size()); + MemMap tmp = image_reservation->TakeReservedMemory(total_size); + tmp.swap(*image_reservation); + // Let the destructor of `tmp` unmap the memory after the chunk we shall use. + } DCHECK(!extra_reservation->IsValid()); if (extra_reservation_size != 0u) { DCHECK_ALIGNED(extra_reservation_size, kPageSize); @@ -1720,6 +2107,10 @@ class ImageSpace::BootImageLoader { return false; } } + uint32_t diff = reinterpret_cast32<uint32_t>(image_reservation->Begin()) - image_start; + image_start += diff; + image_end += diff; + oat_end += diff; DCHECK(!oat_reservation->IsValid()); *oat_reservation = image_reservation->RemapAtEnd(reinterpret_cast32<uint8_t*>(image_end), "Boot image oat reservation", @@ -1838,11 +2229,8 @@ bool ImageSpace::LoadBootImage( const std::string& dalvik_cache = loader.GetDalvikCache(); DCHECK(!dalvik_cache.empty()); std::string local_error_msg; - // All secondary images are verified when the primary image is verified. - bool verified = - VerifyImage(image_location.c_str(), dalvik_cache.c_str(), image_isa, &local_error_msg); bool check_space = CheckSpace(dalvik_cache, &local_error_msg); - if (!verified || !check_space) { + if (!check_space) { LOG(WARNING) << local_error_msg << " Preemptively pruning the dalvik cache."; PruneDalvikCache(image_isa); @@ -1858,27 +2246,9 @@ bool ImageSpace::LoadBootImage( // Collect all the errors. std::vector<std::string> error_msgs; - // Step 1: Check if we have an existing image in the dalvik cache. - if (loader.HasCache()) { - std::string local_error_msg; - // If we have system image, validate system image checksums, otherwise validate the oat file. - if (loader.LoadFromDalvikCache(/* validate_system_checksums */ loader.HasSystem(), - /* validate_oat_file */ !loader.HasSystem(), - extra_reservation_size, - boot_image_spaces, - extra_reservation, - &local_error_msg)) { - return true; - } - error_msgs.push_back(local_error_msg); - } + // Step 1: Check if we have an existing image in /system. - // Step 2: We have an existing image in /system. - - // Step 2.a: We are not required to relocate it. Then we can use it directly. - bool relocate = Runtime::Current()->ShouldRelocate(); - - if (loader.HasSystem() && !relocate) { + if (loader.HasSystem()) { std::string local_error_msg; if (loader.LoadFromSystem(extra_reservation_size, boot_image_spaces, @@ -1889,29 +2259,17 @@ bool ImageSpace::LoadBootImage( error_msgs.push_back(local_error_msg); } - // Step 2.b: We require a relocated image. Then we must patch it. - if (loader.HasSystem() && relocate) { + // Step 2: Check if we have an existing image in the dalvik cache. + if (loader.HasCache()) { std::string local_error_msg; - if (!dex2oat_enabled) { - local_error_msg = "Patching disabled."; - } else if (ImageCreationAllowed(loader.IsGlobalCache(), image_isa, &local_error_msg)) { - bool patch_success = RelocateImage( - image_location.c_str(), loader.GetDalvikCache().c_str(), image_isa, &local_error_msg); - if (patch_success) { - if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false, - /* validate_oat_file */ false, - extra_reservation_size, - boot_image_spaces, - extra_reservation, - &local_error_msg)) { - return true; - } - } + if (loader.LoadFromDalvikCache(/* validate_oat_file= */ true, + extra_reservation_size, + boot_image_spaces, + extra_reservation, + &local_error_msg)) { + return true; } - error_msgs.push_back(StringPrintf("Cannot relocate image %s to %s: %s", - image_location.c_str(), - loader.GetCacheFilename().c_str(), - local_error_msg.c_str())); + error_msgs.push_back(local_error_msg); } // Step 3: We do not have an existing image in /system, @@ -1924,8 +2282,7 @@ bool ImageSpace::LoadBootImage( bool compilation_success = GenerateImage(loader.GetCacheFilename(), image_isa, &local_error_msg); if (compilation_success) { - if (loader.LoadFromDalvikCache(/* validate_system_checksums */ false, - /* validate_oat_file */ false, + if (loader.LoadFromDalvikCache(/* validate_oat_file= */ false, extra_reservation_size, boot_image_spaces, extra_reservation, @@ -1984,13 +2341,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, - /* image_reservation */ nullptr, - /* oat_reservation */ nullptr, - error_msg); + return Loader::InitAppImage(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 a2490acdbb..4db6fdce1d 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -57,9 +57,9 @@ class ImageSpace : public MemMapSpace { // Reads the image header from the specified image location for the // instruction set image_isa. Returns null on failure, with // reason in error_msg. - static ImageHeader* ReadImageHeader(const char* image_location, - InstructionSet image_isa, - std::string* error_msg); + static std::unique_ptr<ImageHeader> ReadImageHeader(const char* image_location, + InstructionSet image_isa, + std::string* error_msg); // Give access to the OatFile. const OatFile* GetOatFile() const; diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 299a413432..cc70788725 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -110,7 +110,7 @@ TEST_F(DexoptTest, ValidateOatFile) { EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); } -template <bool kImage, bool kRelocate, bool kPatchoat, bool kImageDex2oat> +template <bool kImage, bool kRelocate, bool kImageDex2oat> class ImageSpaceLoadingTest : public CommonRuntimeTest { protected: void SetUpRuntimeOptions(RuntimeOptions* options) override { @@ -119,9 +119,6 @@ class ImageSpaceLoadingTest : public CommonRuntimeTest { nullptr); } options->emplace_back(kRelocate ? "-Xrelocate" : "-Xnorelocate", nullptr); - if (!kPatchoat) { - options->emplace_back("-Xpatchoat:false", nullptr); - } options->emplace_back(kImageDex2oat ? "-Ximage-dex2oat" : "-Xnoimage-dex2oat", nullptr); // We want to test the relocation behavior of ImageSpace. As such, don't pretend we're a @@ -130,27 +127,22 @@ class ImageSpaceLoadingTest : public CommonRuntimeTest { } }; -using ImageSpacePatchoatTest = ImageSpaceLoadingTest<true, true, true, true>; -TEST_F(ImageSpacePatchoatTest, Test) { - EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); -} - -using ImageSpaceDex2oatTest = ImageSpaceLoadingTest<false, true, false, true>; +using ImageSpaceDex2oatTest = ImageSpaceLoadingTest<false, true, true>; TEST_F(ImageSpaceDex2oatTest, Test) { EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); } -using ImageSpaceNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest<true, true, false, false>; -TEST_F(ImageSpaceNoDex2oatNoPatchoatTest, Test) { - EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); +using ImageSpaceNoDex2oatTest = ImageSpaceLoadingTest<true, true, false>; +TEST_F(ImageSpaceNoDex2oatTest, Test) { + EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); } -using ImageSpaceNoRelocateNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest<true, false, false, false>; -TEST_F(ImageSpaceNoRelocateNoDex2oatNoPatchoatTest, Test) { +using ImageSpaceNoRelocateNoDex2oatTest = ImageSpaceLoadingTest<true, false, false>; +TEST_F(ImageSpaceNoRelocateNoDex2oatTest, Test) { EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); } -class NoAccessAndroidDataTest : public ImageSpaceLoadingTest<false, true, false, true> { +class NoAccessAndroidDataTest : public ImageSpaceLoadingTest<false, true, true> { protected: void SetUpRuntimeOptions(RuntimeOptions* options) override { const char* android_data = getenv("ANDROID_DATA"); @@ -169,7 +161,7 @@ class NoAccessAndroidDataTest : public ImageSpaceLoadingTest<false, true, false, CHECK_NE(fd, -1) << strerror(errno); result = close(fd); CHECK_EQ(result, 0) << strerror(errno); - ImageSpaceLoadingTest<false, true, false, true>::SetUpRuntimeOptions(options); + ImageSpaceLoadingTest<false, true, true>::SetUpRuntimeOptions(options); } void TearDown() override { @@ -179,7 +171,7 @@ class NoAccessAndroidDataTest : public ImageSpaceLoadingTest<false, true, false, CHECK_EQ(result, 0) << strerror(errno); result = setenv("ANDROID_DATA", old_android_data_.c_str(), /* replace */ 1); CHECK_EQ(result, 0) << strerror(errno); - ImageSpaceLoadingTest<false, true, false, true>::TearDown(); + ImageSpaceLoadingTest<false, true, true>::TearDown(); } private: diff --git a/runtime/image-inl.h b/runtime/image-inl.h index c527f6fbcc..9fde669a49 100644 --- a/runtime/image-inl.h +++ b/runtime/image-inl.h @@ -49,6 +49,38 @@ inline ObjPtr<mirror::ObjectArray<mirror::Object>> ImageHeader::GetImageRoots() return image_roots; } +inline void ImageHeader::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const { + const ImageSection& fields = GetFieldsSection(); + for (size_t pos = 0; pos < fields.Size(); ) { + auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + fields.Offset() + pos); + for (size_t i = 0; i < array->size(); ++i) { + visitor->Visit(&array->At(i, sizeof(ArtField))); + } + pos += array->ComputeSize(array->size()); + } +} + +inline void ImageHeader::VisitPackedArtMethods(ArtMethodVisitor* visitor, + uint8_t* base, + PointerSize pointer_size) const { + const size_t method_alignment = ArtMethod::Alignment(pointer_size); + const size_t method_size = ArtMethod::Size(pointer_size); + const ImageSection& methods = GetMethodsSection(); + for (size_t pos = 0; pos < methods.Size(); ) { + auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + methods.Offset() + pos); + for (size_t i = 0; i < array->size(); ++i) { + visitor->Visit(&array->At(i, method_size, method_alignment)); + } + pos += array->ComputeSize(array->size(), method_size, method_alignment); + } + const ImageSection& runtime_methods = GetRuntimeMethodsSection(); + for (size_t pos = 0; pos < runtime_methods.Size(); ) { + auto* method = reinterpret_cast<ArtMethod*>(base + runtime_methods.Offset() + pos); + visitor->Visit(method); + pos += method_size; + } +} + template <typename Visitor> inline void ImageHeader::VisitPackedImTables(const Visitor& visitor, uint8_t* base, diff --git a/runtime/image.cc b/runtime/image.cc index 028c515c91..bdf045bd19 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -77,7 +77,7 @@ ImageHeader::ImageHeader(uint32_t image_begin, std::copy_n(sections, kSectionCount, sections_); } -void ImageHeader::RelocateImage(off_t delta) { +void ImageHeader::RelocateImage(int64_t delta) { CHECK_ALIGNED(delta, kPageSize) << " patch delta must be page aligned"; oat_file_begin_ += delta; oat_data_begin_ += delta; @@ -88,12 +88,12 @@ void ImageHeader::RelocateImage(off_t delta) { RelocateImageMethods(delta); } -void ImageHeader::RelocateImageObjects(off_t delta) { +void ImageHeader::RelocateImageObjects(int64_t delta) { image_begin_ += delta; image_roots_ += delta; } -void ImageHeader::RelocateImageMethods(off_t delta) { +void ImageHeader::RelocateImageMethods(int64_t delta) { for (size_t i = 0; i < kImageMethodsCount; ++i) { image_methods_[i] += delta; } @@ -152,38 +152,6 @@ void ImageHeader::VisitObjects(ObjectVisitor* visitor, } } -void ImageHeader::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const { - const ImageSection& fields = GetFieldsSection(); - for (size_t pos = 0; pos < fields.Size(); ) { - auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + fields.Offset() + pos); - for (size_t i = 0; i < array->size(); ++i) { - visitor->Visit(&array->At(i, sizeof(ArtField))); - } - pos += array->ComputeSize(array->size()); - } -} - -void ImageHeader::VisitPackedArtMethods(ArtMethodVisitor* visitor, - uint8_t* base, - PointerSize pointer_size) const { - const size_t method_alignment = ArtMethod::Alignment(pointer_size); - const size_t method_size = ArtMethod::Size(pointer_size); - const ImageSection& methods = GetMethodsSection(); - for (size_t pos = 0; pos < methods.Size(); ) { - auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + methods.Offset() + pos); - for (size_t i = 0; i < array->size(); ++i) { - visitor->Visit(&array->At(i, method_size, method_alignment)); - } - pos += array->ComputeSize(array->size(), method_size, method_alignment); - } - const ImageSection& runtime_methods = GetRuntimeMethodsSection(); - for (size_t pos = 0; pos < runtime_methods.Size(); ) { - auto* method = reinterpret_cast<ArtMethod*>(base + runtime_methods.Offset() + pos); - visitor->Visit(method); - pos += method_size; - } -} - PointerSize ImageHeader::GetPointerSize() const { return ConvertToPointerSize(pointer_size_); } diff --git a/runtime/image.h b/runtime/image.h index af092ad3fe..6acb64b18e 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -175,11 +175,11 @@ class PACKED(4) ImageHeader { return pointer_size_; } - off_t GetPatchDelta() const { + int32_t GetPatchDelta() const { return patch_delta_; } - void SetPatchDelta(off_t patch_delta) { + void SetPatchDelta(int32_t patch_delta) { patch_delta_ = patch_delta; } @@ -299,9 +299,9 @@ class PACKED(4) ImageHeader { ObjPtr<mirror::ObjectArray<mirror::Object>> GetImageRoots() const REQUIRES_SHARED(Locks::mutator_lock_); - void RelocateImage(off_t delta); - void RelocateImageMethods(off_t delta); - void RelocateImageObjects(off_t delta); + void RelocateImage(int64_t delta); + void RelocateImageMethods(int64_t delta); + void RelocateImageObjects(int64_t delta); bool CompilePic() const { return compile_pic_ != 0; diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 5ba3e189ba..00b947a5a7 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -185,6 +185,11 @@ class InternTable { return item.IsNull(); } }; + using UnorderedSet = HashSet<GcRoot<mirror::String>, + GcRootEmptyFn, + StringHashEquals, + StringHashEquals, + TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>>; // Table which holds pre zygote and post zygote interned strings. There is one instance for // weak interns and strong interns. @@ -217,9 +222,6 @@ class InternTable { REQUIRES(Locks::intern_table_lock_) REQUIRES_SHARED(Locks::mutator_lock_); private: - typedef HashSet<GcRoot<mirror::String>, GcRootEmptyFn, StringHashEquals, StringHashEquals, - TrackingAllocator<GcRoot<mirror::String>, kAllocatorTagInternTable>> UnorderedSet; - void SweepWeaks(UnorderedSet* set, IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); @@ -287,6 +289,7 @@ class InternTable { // Weak root state, used for concurrent system weak processing and more. gc::WeakRootState weak_root_state_ GUARDED_BY(Locks::intern_table_lock_); + friend class gc::space::ImageSpace; friend class linker::ImageWriter; friend class Transaction; ART_FRIEND_TEST(InternTableTest, CrossHash); diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc index 8b4fe44c15..b3bf1ba41c 100644 --- a/runtime/intern_table_test.cc +++ b/runtime/intern_table_test.cc @@ -78,7 +78,7 @@ TEST_F(InternTableTest, CrossHash) { GcRoot<mirror::String> str(mirror::String::AllocFromModifiedUtf8(soa.Self(), "00000000")); MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - for (InternTable::Table::UnorderedSet& table : t.strong_interns_.tables_) { + for (InternTable::UnorderedSet& table : t.strong_interns_.tables_) { // The negative hash value shall be 32-bit wide on every host. ASSERT_TRUE(IsUint<32>(table.hashfn_(str))); } diff --git a/runtime/mirror/executable.cc b/runtime/mirror/executable.cc index fac33192e4..24e2047bed 100644 --- a/runtime/mirror/executable.cc +++ b/runtime/mirror/executable.cc @@ -38,18 +38,6 @@ template bool Executable::CreateFromArtMethod<PointerSize::k32, true>(ArtMethod* template bool Executable::CreateFromArtMethod<PointerSize::k64, false>(ArtMethod* method); template bool Executable::CreateFromArtMethod<PointerSize::k64, true>(ArtMethod* method); -ArtMethod* Executable::GetArtMethod() { - return reinterpret_cast<ArtMethod*>(GetField64(ArtMethodOffset())); -} - -template <bool kTransactionActive> -void Executable::SetArtMethod(ArtMethod* method) { - SetField64<kTransactionActive>(ArtMethodOffset(), reinterpret_cast<uint64_t>(method)); -} - -template void Executable::SetArtMethod<false>(ArtMethod* method); -template void Executable::SetArtMethod<true>(ArtMethod* method); - mirror::Class* Executable::GetDeclaringClass() { return GetFieldObject<mirror::Class>(DeclaringClassOffset()); } diff --git a/runtime/mirror/executable.h b/runtime/mirror/executable.h index bf66d7952a..14c9d4c96f 100644 --- a/runtime/mirror/executable.h +++ b/runtime/mirror/executable.h @@ -18,7 +18,7 @@ #define ART_RUNTIME_MIRROR_EXECUTABLE_H_ #include "accessible_object.h" -#include "object.h" +#include "object-inl.h" #include "read_barrier_option.h" namespace art { @@ -36,10 +36,19 @@ class MANAGED Executable : public AccessibleObject { bool CreateFromArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - ArtMethod* GetArtMethod() REQUIRES_SHARED(Locks::mutator_lock_); - // Only used by the image writer. - template <bool kTransactionActive = false> - void SetArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ArtMethod* GetArtMethod() REQUIRES_SHARED(Locks::mutator_lock_) { + return reinterpret_cast64<ArtMethod*>(GetField64<kVerifyFlags>(ArtMethodOffset())); + } + + template <bool kTransactionActive = false, + bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void SetArtMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>( + ArtMethodOffset(), reinterpret_cast64<uint64_t>(method)); + } + mirror::Class* GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_); static MemberOffset ArtMethodOffset() { diff --git a/runtime/mirror/object-refvisitor-inl.h b/runtime/mirror/object-refvisitor-inl.h index 748f03b862..f0bee5a416 100644 --- a/runtime/mirror/object-refvisitor-inl.h +++ b/runtime/mirror/object-refvisitor-inl.h @@ -33,8 +33,8 @@ template <bool kVisitNativeRoots, typename JavaLangRefVisitor> inline void Object::VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) { + visitor(this, ClassOffset(), /* is_static= */ false); ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>(); - visitor(this, ClassOffset(), false); const uint32_t class_flags = klass->GetClassFlags<kVerifyNone>(); if (LIKELY(class_flags == kClassFlagNormal)) { DCHECK((!klass->IsVariableSize<kVerifyFlags>())); diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h index 496a6f3d09..439f4856a6 100644 --- a/runtime/noop_compiler_callbacks.h +++ b/runtime/noop_compiler_callbacks.h @@ -31,11 +31,6 @@ class NoopCompilerCallbacks final : public CompilerCallbacks { void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) override {} - // This is only used by compilers which need to be able to run without relocation even when it - // would normally be enabled. For example the patchoat executable, and dex2oat --image, both need - // to disable the relocation since both deal with writing out the images directly. - bool IsRelocationPossible() override { return false; } - verifier::VerifierDeps* GetVerifierDeps() const override { return nullptr; } private: diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h index f1e3b5053b..b949c96dd2 100644 --- a/runtime/obj_ptr-inl.h +++ b/runtime/obj_ptr-inl.h @@ -24,18 +24,27 @@ namespace art { template<class MirrorType> +inline uintptr_t ObjPtr<MirrorType>::GetCurrentTrimedCookie() { + Thread* self = Thread::Current(); + if (UNLIKELY(self == nullptr)) { + return kCookieMask; + } + return self->GetPoisonObjectCookie() & kCookieMask; +} + +template<class MirrorType> inline bool ObjPtr<MirrorType>::IsValid() const { if (!kObjPtrPoisoning || IsNull()) { return true; } - return GetCookie() == TrimCookie(Thread::Current()->GetPoisonObjectCookie()); + return GetCookie() == GetCurrentTrimedCookie(); } template<class MirrorType> inline void ObjPtr<MirrorType>::AssertValid() const { if (kObjPtrPoisoning) { CHECK(IsValid()) << "Stale object pointer " << PtrUnchecked() << " , expected cookie " - << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie(); + << GetCurrentTrimedCookie() << " but got " << GetCookie(); } } @@ -47,9 +56,7 @@ inline uintptr_t ObjPtr<MirrorType>::Encode(MirrorType* ptr) { DCHECK_LE(ref, 0xFFFFFFFFU); ref >>= kObjectAlignmentShift; // Put cookie in high bits. - Thread* self = Thread::Current(); - DCHECK(self != nullptr); - ref |= self->GetPoisonObjectCookie() << kCookieShift; + ref |= GetCurrentTrimedCookie() << kCookieShift; } return ref; } diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index e421d878ff..60e21ab3b5 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -156,9 +156,7 @@ class ObjPtr { private: // Trim off high bits of thread local cookie. - ALWAYS_INLINE static uintptr_t TrimCookie(uintptr_t cookie) { - return cookie & kCookieMask; - } + ALWAYS_INLINE static uintptr_t GetCurrentTrimedCookie(); ALWAYS_INLINE uintptr_t GetCookie() const { return reference_ >> kCookieShift; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 6878cc08c8..a51d457ef9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2499,7 +2499,7 @@ void Runtime::CreateJit() { } bool Runtime::CanRelocate() const { - return !IsAotCompiler() || compiler_callbacks_->IsRelocationPossible(); + return !IsAotCompiler(); } bool Runtime::IsCompilingBootImage() const { diff --git a/test/119-noimage-patchoat/check b/test/119-noimage-patchoat/check deleted file mode 100755 index d124ce8cfd..0000000000 --- a/test/119-noimage-patchoat/check +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Strip the process pids and line numbers from exact error messages. -sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp" - -diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/119-noimage-patchoat/expected.txt b/test/119-noimage-patchoat/expected.txt deleted file mode 100644 index 9b9db58fcd..0000000000 --- a/test/119-noimage-patchoat/expected.txt +++ /dev/null @@ -1,11 +0,0 @@ -Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -JNI_OnLoad called -Has image is false, is image dex2oat enabled is false. -Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback -Failed to initialize runtime (check log for details) -Run -Ximage-dex2oat -JNI_OnLoad called -Has image is true, is image dex2oat enabled is true. -Run default -JNI_OnLoad called -Has image is true, is image dex2oat enabled is true. diff --git a/test/119-noimage-patchoat/info.txt b/test/119-noimage-patchoat/info.txt deleted file mode 100644 index 6b853688dd..0000000000 --- a/test/119-noimage-patchoat/info.txt +++ /dev/null @@ -1 +0,0 @@ -Test that disables patchoat'ing the image. diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run deleted file mode 100644 index 497dc4ad88..0000000000 --- a/test/119-noimage-patchoat/run +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -flags="$@" - -# Force relocation otherwise we will just use the already created core.oat/art pair. -# Note: relocate is the default. -if [[ "${flags}" == *--no-relocate* ]] ; then - echo "Test 119-noimage-patchoat is not intended to run in no-relocate mode." - exit 1 -fi - -if [[ $@ == *--host* ]]; then - false_bin="/bin/false" -else - false_bin="/system/bin/false" -fi - -# Make sure we can run without an image file. -echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false" -${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \ - --runtime-option -Xpatchoat:${false_bin} -return_status1=$? - -# Make sure we cannot run without an image file without fallback. -echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback" -${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \ - --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback -# This second run is expected to fail: invert the return status of the previous command. -return_status2=$((! $?)) - -# Make sure we can run with the image file. -echo "Run -Ximage-dex2oat" -${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat -return_status3=$? - -# Make sure we can run with the default settings. -echo "Run default" -${RUN} ${flags} ${BPATH} -return_status4=$? - -# Make sure we don't silently ignore an early failure. -(exit $return_status1) && (exit $return_status2) && (exit $return_status3) && (exit $return_status4) diff --git a/test/119-noimage-patchoat/src/Main.java b/test/119-noimage-patchoat/src/Main.java deleted file mode 100644 index 6a70f5885b..0000000000 --- a/test/119-noimage-patchoat/src/Main.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -public class Main { - public static void main(String[] args) { - System.loadLibrary(args[0]); - boolean hasImage = hasImage(); - System.out.println( - "Has image is " + hasImage + ", is image dex2oat enabled is " - + isImageDex2OatEnabled() + "."); - - if (hasImage && !isImageDex2OatEnabled()) { - throw new Error("Image with dex2oat disabled runs with an oat file"); - } else if (!hasImage && isImageDex2OatEnabled()) { - throw new Error("Image with dex2oat enabled runs without an oat file"); - } - } - - private native static boolean hasImage(); - - private native static boolean isImageDex2OatEnabled(); -} diff --git a/test/knownfailures.json b/test/knownfailures.json index 8ca0012282..fc4b25fa2b 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -77,8 +77,7 @@ }, { "tests": ["117-nopatchoat", - "118-noimage-dex2oat", - "119-noimage-patchoat"], + "118-noimage-dex2oat"], "variant": "no-relocate", "description": ["117-nopatchoat is not broken per-se it just doesn't", "work (and isn't meant to) without --prebuild", @@ -181,7 +180,6 @@ "tests": ["116-nodex2oat", "117-nopatchoat", "118-noimage-dex2oat", - "119-noimage-patchoat", "137-cfi", "138-duplicate-classes-check2"], "variant": "no-image | relocate-npatchoat", @@ -343,7 +341,6 @@ "116-nodex2oat", "117-nopatchoat", "118-noimage-dex2oat", - "119-noimage-patchoat", "126-miranda-multidex", "137-cfi"], "description": "The test run dalvikvm more than once.", @@ -772,7 +769,6 @@ "116-nodex2oat", "117-nopatchoat", "118-noimage-dex2oat", - "119-noimage-patchoat", "127-checker-secondarydex", "129-ThreadGetId", "130-hprof", |