summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2018-09-27 16:42:44 +0000
committer Vladimir Marko <vmarko@google.com> 2018-09-28 09:12:06 +0100
commit4df2d8041f5dcc7af8c3b3b60b0ea87a1e0d3b94 (patch)
tree0273072a2b65d6c0cf692a3e7f8eab9814d9d23d
parent233b572a940431a94a1790750afdceab2d6f4fde (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
-rw-r--r--compiler/dex/quick_compiler_callbacks.h5
-rw-r--r--compiler/verifier_deps_test.cc1
-rw-r--r--imgdiag/imgdiag.cc2
-rw-r--r--runtime/art_method.h4
-rw-r--r--runtime/compiler_callbacks.h4
-rw-r--r--runtime/gc/heap.cc19
-rw-r--r--runtime/gc/space/image_space.cc997
-rw-r--r--runtime/gc/space/image_space.h6
-rw-r--r--runtime/gc/space/image_space_test.cc28
-rw-r--r--runtime/image-inl.h32
-rw-r--r--runtime/image.cc38
-rw-r--r--runtime/image.h10
-rw-r--r--runtime/intern_table.h9
-rw-r--r--runtime/intern_table_test.cc2
-rw-r--r--runtime/mirror/executable.cc12
-rw-r--r--runtime/mirror/executable.h19
-rw-r--r--runtime/mirror/object-refvisitor-inl.h2
-rw-r--r--runtime/noop_compiler_callbacks.h5
-rw-r--r--runtime/obj_ptr-inl.h17
-rw-r--r--runtime/obj_ptr.h4
-rw-r--r--runtime/runtime.cc2
-rwxr-xr-xtest/119-noimage-patchoat/check20
-rw-r--r--test/119-noimage-patchoat/expected.txt11
-rw-r--r--test/119-noimage-patchoat/info.txt1
-rw-r--r--test/119-noimage-patchoat/run56
-rw-r--r--test/119-noimage-patchoat/src/Main.java35
-rw-r--r--test/knownfailures.json6
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",