namespace art HIDDEN { namespace gc { namespace space {
namespace {
using ::android::base::Join; using ::android::base::StringAppendF; using ::android::base::StringPrintf;
// We do not allow the boot image and extensions to take more than 1GiB. They are // supposed to be much smaller and allocating more that this would likely fail anyway. static constexpr size_t kMaxTotalImageReservationSize = 1 * GB;
for (size_t i = base_image_space_count, size = spaces.size(); i != size; ) { const ImageHeader& ext_header = spaces[i]->GetImageHeader();
size_t ext_image_space_count = ext_header.GetImageSpaceCount();
DCHECK_LE(ext_image_space_count, size - i);
RelocateImage<kPointerSize>(spaces.SubArray(/*pos=*/ i, ext_image_space_count),
base_diff64,
&patched_objects);
i += ext_image_space_count;
}
}
// Relocate an app image mapped at `target_base` which may have been built // with a different base address. template <PointerSize kPointerSize> staticvoid RelocateImage(ArrayRef<const std::unique_ptr<ImageSpace>> spaces,
int64_t base_diff64,
gc::accounting::ContinuousSpaceBitmap* patched_objects);
private: template <PointerSize kPointerSize, typename Visitor> class PatchObjectVisitor;
template <PointerSize kPointerSize, typename Visitor> class ImageSpace::Relocator::PatchObjectVisitor final { public: explicit PatchObjectVisitor(Visitor visitor)
: visitor_(visitor) {}
void VisitClass(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Class> class_class)
REQUIRES_SHARED(Locks::mutator_lock_) { // A mirror::Class object consists of // - instance fields inherited from j.l.Object, // - instance fields inherited from j.l.Class, // - embedded tables (vtable, interface method table), // - static fields of the class itself. // The reference fields are at the start of each field section (this is how the // ClassLinker orders fields; except when that would create a gap between superclass // fields and the first reference of the subclass due to alignment, it can be filled // with smaller fields - but that's not the case for j.l.Object and j.l.Class).
DCHECK_ALIGNED(klass.Ptr(), kObjectAlignment);
static_assert(IsAligned<kHeapReferenceSize>(kObjectAlignment), "Object alignment check."); // 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.
DCHECK_EQ(class_class, visitor_(klass->GetClass<kVerifyNone, kWithoutReadBarrier>()));
klass->SetFieldObjectWithoutWriteBarrier< /*kTransactionActive=*/ false, /*kCheckTransaction=*/ true,
kVerifyNone>(mirror::Object::ClassOffset(), class_class); // Then patch the reference instance fields described by j.l.Class.class. // Use the sizeof(Object) to determine where these reference fields start; // this is the same as `class_class->GetFirstReferenceInstanceFieldOffset()` // after patching but the j.l.Class may not have been patched yet.
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);
static_assert(sizeof(mirror::HeapReference<mirror::Object>) == kHeapReferenceSize, "Heap reference sizes equality check.");
instance_field_offset =
MemberOffset(instance_field_offset.Uint32Value() + kHeapReferenceSize);
} // 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) {
ObjPtr<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_assert(sizeof(mirror::HeapReference<mirror::Object>) == kHeapReferenceSize, "Heap reference sizes equality check.");
static_field_offset =
MemberOffset(static_field_offset.Uint32Value() + kHeapReferenceSize);
}
} // Then patch native pointers.
klass->FixupNativePointers<kVerifyNone>(klass.Ptr(), kPointerSize, *this);
}
void VisitPointerArray(ObjPtr<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</*kMayBeNull=*/ false>(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 voidoperator()(ObjPtr<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 voidoperator()(ObjPtr<mirror::Class> klass, ObjPtr<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(
[[maybe_unused]] mirror::CompressedReference<mirror::Object>* root) const {} void VisitRoot([[maybe_unused]] mirror::CompressedReference<mirror::Object>* root) const {}
template <PointerSize kPointerSize> void ImageSpace::Relocator::RelocateImage(ArrayRef<const std::unique_ptr<ImageSpace>> spaces,
int64_t base_diff64,
gc::accounting::ContinuousSpaceBitmap* patched_objects) { // Note: This function visits objects of the image that's not part of the heap yet and reads // only constant data from the boot image, so it does not need the mutator lock. However, // since it's using many functions annotated with `REQUIRES_SHARED(Locks::mutator_lock_)`, // pretend that we acquire the reader access for the static analysis.
FakeReaderMutexLock fake_lock(*Locks::mutator_lock_);
// Prepare source ranges. The layout recorded in the image header always specifies the // boot image it depends on first (except the primary boot image which has no dependency), // followed immediatelly by the image and oat files. For boot images, the oat file ranges // are included in the image reservation, for an app oat file the oat file is outside the // reservation if present (there is no oat file for a runtime app image). const uint32_t image_begin = reinterpret_cast32<uint32_t>(first_header.GetImageBegin());
uint32_t image_size = first_header.GetImageReservationSize();
DCHECK_NE(image_size, 0u); const uint32_t source_begin = is_primary ? image_begin : first_header.GetBootImageBegin(); const uint32_t boot_image_size = is_primary ? image_size : first_header.GetBootImageSize();
DCHECK_IMPLIES(!is_primary, source_begin + boot_image_size == image_begin);
uint32_t source_size = (is_primary ? 0u : boot_image_size) + image_size;
uint32_t oat_files_end = reinterpret_cast32<uint32_t>(last_header.GetOatDataEnd());
uint32_t oat_source_size = oat_files_end != 0u ? oat_files_end - source_begin : boot_image_size;
// True if we need to fixup any pointers. We always fix up app oat file pointers. constbool fixup_image = base_diff != 0u || image_diff != 0u || has_app_oat_files; if (!fixup_image) { // Nothing to fix up. return;
}
for (const std::unique_ptr<ImageSpace>& space : spaces) { const ImageHeader& image_header = space->GetImageHeader(); if (kIsDebugBuild) {
ObjPtr<mirror::ObjectArray<mirror::Object>> image_roots =
forward_current_image(image_header.GetImageRoots<kWithoutReadBarrier>().Ptr());
CHECK_EQ(class_roots,
forward_boot_image(image_roots->GetWithoutChecks<
kVerifyNone, kWithoutReadBarrier>(class_roots_index).Ptr())); // For primary boot image, class roots have not been patched yet.
CHECK_IMPLIES(is_primary, !patched_objects->Test(class_roots.Ptr())); // For boot image extension, class roots have already been patched if primary was relocated.
CHECK_IMPLIES(!is_primary && !is_app_image && base_diff != 0u,
patched_objects->Test(class_roots.Ptr())); // For app image, the `patched_objects` does not cover the primary boot image.
}
// Calculate oat file diff before patching the image header.
uintptr_t oat_diff = static_cast<uintptr_t>(
space->oat_file_non_owned_->Begin() - space->GetImageHeader().GetOatDataBegin());
// Patch the image header.
DCHECK(forward_current_image.InSource(image_header.GetImageRoots<kWithoutReadBarrier>().Ptr())); const_cast<ImageHeader&>(image_header).RelocateImageReferences(image_diff64); const_cast<ImageHeader&>(image_header).RelocateBootImageReferences(base_diff64);
DCHECK_EQ(image_header.GetImageBegin(), space->Begin());
// Patch fields and methods.
{
TimingLogger::ScopedTiming timing("Fixup fields", &logger);
image_header.VisitPackedArtFields(
[&](ArtField& field) REQUIRES_SHARED(Locks::mutator_lock_) { // Fields always reference class in the current image.
patch_current_image_visitor.template PatchGcRoot</*kMayBeNull=*/ false>(
&field.DeclaringClassRoot());
},
space->Begin());
}
{
TimingLogger::ScopedTiming timing("Fixup methods", &logger);
SplitRangeRelocateVisitor forward_code(
source_begin, oat_source_size, image_begin, base_diff, oat_diff);
VLOG(image) << "Code forwarding: " << Dumpable<SplitRangeRelocateVisitor>(forward_code);
PatchObjectVisitor<kPointerSize, SplitRangeRelocateVisitor> patch_code_visitor(forward_code);
image_header.VisitPackedArtMethods(
[&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { // TODO: Consider a separate visitor for runtime vs normal methods. void** data_address = PointerAddress(&method, ArtMethod::DataOffset(kPointerSize)); void** entrypoint_address = PointerAddress(
&method, ArtMethod::EntryPointFromQuickCompiledCodeOffset(kPointerSize)); if (UNLIKELY(method.IsRuntimeMethod())) {
DCHECK(method.GetDeclaringClass<kWithoutReadBarrier>() == nullptr); // Patch IMT conflict table pointer if any.
patch_object_visitor.PatchNativePointer(data_address); // Patch entrypoint. Transition methods (in primary boot image) have null entrypoint.
patch_code_visitor.PatchNativePointer(entrypoint_address);
} else {
patch_object_visitor.PatchGcRoot(&method.DeclaringClassRoot());
uint32_t access_flags = method.GetAccessFlags(); if (ArtMethod::IsNative(access_flags)) {
DCHECK(ArtMethod::IsInvokable(access_flags)); // Unlinked JNI entrypoint points to a trampoline in the boot image.
patch_boot_image_visitor.template PatchNativePointer</*kMayBeNull=*/ false>(
data_address);
} elseif (ArtMethod::IsInvokable(access_flags)) { // The code item offset shall be changed to code item pointer by `ClassLinker`. // TODO: Can we do this here to reduce the work we do with the mutator lock held?
DCHECK(method.HasCodeItem());
} else {
DCHECK(!method.HasCodeItem()); // TODO: Relocate single-implementation pointer, if any, with // patch_object_visitor.PatchNativePointer( // PointerAddress(&method, ArtMethod::DataOffset(kPointerSize))); // There's a corresponding TODO where we clear it in `ImageWriter`. // The runtime image also clears it but without a TODO comment. using StorageType =
std::conditional_t<kPointerSize == PointerSize::k64, uint64_t, uint32_t>;
DCHECK_EQ(*reinterpret_cast<const StorageType*>(data_address), 0u);
}
patch_code_visitor.template PatchNativePointer</*kMayBeNull=*/ false>(
entrypoint_address);
}
},
space->Begin(),
kPointerSize);
}
{
TimingLogger::ScopedTiming timing("Fixup imt", &logger);
image_header.VisitPackedImTables(forward_image, space->Begin(), kPointerSize);
}
{
TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
image_header.VisitPackedImtConflictTables(forward_image, space->Begin(), kPointerSize);
} if (!is_app_image) {
TimingLogger::ScopedTiming timing("Fixup JNI stub methods", &logger);
image_header.VisitJniStubMethods</*kUpdate=*/ true>(
forward_image, space->Begin(), kPointerSize);
}
// Fix up the intern table. constauto& intern_table_section = image_header.GetInternedStringsSection(); if (intern_table_section.Size() != 0u) {
TimingLogger::ScopedTiming timing("Fixup intern table", &logger); const uint8_t* data = space->Begin() + intern_table_section.Offset();
size_t read_count;
InternTable::UnorderedSet temp_set(data, /*make_copy_of_data=*/ false, &read_count); for (GcRoot<mirror::String>& root : temp_set) { // The intern table contains only strings in the current image.
root = GcRoot<mirror::String>(forward_current_image(root.Read<kWithoutReadBarrier>()));
}
}
// 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. // The `patched_objects` bitmap is used to ensure that pointer arrays are not forwarded twice.
{
TimingLogger::ScopedTiming timing("Fixup classes", &logger); constauto& class_table_section = image_header.GetClassTableSection(); if (class_table_section.Size() > 0u) {
ClassTableVisitor class_table_visitor(forward_image);
size_t read_count = 0u; const uint8_t* data = space->Begin() + class_table_section.Offset(); // We avoid making a copy of the data since we want modifications to be propagated to the // memory map.
ClassTable::ClassSet temp_set(data, /*make_copy_of_data=*/ false, &read_count); for (ClassTable::TableSlot& slot : temp_set) {
slot.VisitRoot(class_table_visitor);
ObjPtr<mirror::Class> klass = slot.Read<kWithoutReadBarrier>(); if (!forward_current_image.InDest(klass.Ptr())) { continue;
} constbool already_marked = patched_objects->Set(klass.Ptr());
CHECK(!already_marked) << "App image class already visited";
patch_object_visitor.VisitClass(klass, class_class); // Then patch the non-embedded vtable and iftable.
ObjPtr<mirror::PointerArray> vtable =
klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(); if (vtable != nullptr &&
forward_current_image.InDest(vtable.Ptr()) &&
!patched_objects->Set(vtable.Ptr())) {
patch_object_visitor.VisitPointerArray(vtable);
}
ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); if (iftable != nullptr && forward_current_image.InDest(iftable.Ptr())) { // Avoid processing the fields of iftable since we will process them later anyways // below.
int32_t ifcount = klass->GetIfTableCount<kVerifyNone>(); for (int32_t i = 0; i != ifcount; ++i) {
ObjPtr<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.
ObjPtr<mirror::PointerArray> ifarray = forward_image(unpatched_ifarray.Ptr()); if (forward_current_image.InDest(ifarray.Ptr()) &&
!patched_objects->Set(ifarray.Ptr())) {
patch_object_visitor.VisitPointerArray(ifarray);
}
}
}
}
}
}
}
}
if (is_primary) {
DCHECK(!patched_objects->Test(class_roots.Ptr()));
patched_objects->Set(class_roots.Ptr());
patch_object_visitor.VisitObject(class_roots.Ptr());
}
// Retrieve the `Method`, `Constructor`, `FieldVarHadle` and `StaticFieldVarHandle` classes // needed for checking if we need to relocate native pointers in their instances.
ObjPtr<mirror::Class> method_class =
GetClassRoot<mirror::Method, kWithoutReadBarrier>(class_roots);
ObjPtr<mirror::Class> constructor_class =
GetClassRoot<mirror::Constructor, kWithoutReadBarrier>(class_roots);
ObjPtr<mirror::Class> field_var_handle_class =
GetClassRoot<mirror::FieldVarHandle, kWithoutReadBarrier>(class_roots);
ObjPtr<mirror::Class> static_field_var_handle_class =
GetClassRoot<mirror::StaticFieldVarHandle, kWithoutReadBarrier>(class_roots);
for (const std::unique_ptr<ImageSpace>& space : spaces) {
TimingLogger::ScopedTiming timing("Fixup objects", &logger); // Need to update the image to be at the target base.
accounting::ContinuousSpaceBitmap* bitmap = space->GetLiveBitmap();
DCHECK(bitmap != nullptr); const ImageHeader& image_header = space->GetImageHeader(); const ImageSection& objects_section = image_header.GetObjectsSection();
bitmap->VisitMarkedRange( reinterpret_cast<uintptr_t>(space->Begin() + objects_section.Offset()), reinterpret_cast<uintptr_t>(space->Begin() + objects_section.End()),
[&](mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE { // Note: Use Test() rather than Set() as this is the last time we're checking this object. if (!patched_objects->Test(object)) {
patch_object_visitor.VisitObject(object);
ObjPtr<mirror::Class> klass = object->GetClass<kVerifyNone, kWithoutReadBarrier>(); if (klass == method_class || klass == constructor_class) { // Patch the ArtMethod* in the mirror::Executable subobject.
ObjPtr<mirror::Executable> as_executable =
ObjPtr<mirror::Executable>::DownCast(object);
ArtMethod* unpatched_method = as_executable->GetArtMethod<kVerifyNone>(); // Only constructors created using serializationCopy might have a null ArtMethod. // Serialized constructors maintain the corresponding method differently and // ArtMethod will be set to nullptr.
DCHECK_IMPLIES(klass == method_class, unpatched_method != nullptr);
ArtMethod* patched_method =
(unpatched_method == nullptr) ? nullptr : forward_image(unpatched_method);
as_executable->SetArtMethod</*kTransactionActive=*/ false, /*kCheckTransaction=*/ true,
kVerifyNone>(patched_method);
} elseif (klass == field_var_handle_class || klass == static_field_var_handle_class) { // Patch the ArtField* in the mirror::FieldVarHandle subobject.
ObjPtr<mirror::FieldVarHandle> as_field_var_handle =
ObjPtr<mirror::FieldVarHandle>::DownCast(object);
ArtField* unpatched_field = as_field_var_handle->GetArtField<kVerifyNone>();
ArtField* patched_field = forward_image(unpatched_field);
as_field_var_handle->SetArtField<kVerifyNone>(patched_field);
}
}
});
// Fix up dex cache arrays.
ObjPtr<mirror::ObjectArray<mirror::DexCache>> dex_caches =
image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)
->AsObjectArray<mirror::DexCache, kVerifyNone>(); for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
ObjPtr<mirror::DexCache> dex_cache =
dex_caches->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(i);
patch_object_visitor.VisitDexCacheArrays(dex_cache);
}
} if (VLOG_IS_ON(image)) {
logger.Dump(LOG_STREAM(INFO));
}
}
// Helper class encapsulating loading, so we can access private ImageSpace members (this is a // nested class), but not declare functions in the header. class ImageSpace::Loader { public: static std::unique_ptr<ImageSpace> InitAppImage(constchar* image_filename, constchar* image_location, const OatFile* oat_file,
ArrayRef<ImageSpace* const> boot_image_spaces, /*out*/std::string* error_msg)
REQUIRES(!Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
DCHECK_LE(boot_image_space_dependencies, boot_image_spaces.size()); if (boot_image_space_dependencies != boot_image_spaces.size()) {
TimingLogger::ScopedTiming timing("DeduplicateInternedStrings", &logger); // There shall be no duplicates with boot image spaces this app image depends on.
ArrayRef<ImageSpace* const> old_spaces =
boot_image_spaces.SubArray(/*pos=*/ boot_image_space_dependencies);
SafeMap<mirror::String*, mirror::String*> intern_remap; // Note: This `space` is not yet part of the heap and we're reading only // constant data from the boot image, so we do not really need the mutator // lock here. Pretend to acquire the reader access for static analysis.
FakeReaderMutexLock fake_lock(*Locks::mutator_lock_);
RemoveInternTableDuplicates(old_spaces, space.get(), &intern_remap); if (!intern_remap.empty()) {
RemapInternedStringDuplicates(intern_remap, space.get());
}
}
const ImageHeader& primary_header = boot_image_spaces.front()->GetImageHeader();
static_assert(static_cast<size_t>(ImageHeader::kResolutionMethod) == 0u); for (size_t i = 0u; i != static_cast<size_t>(ImageHeader::kImageMethodsCount); ++i) {
ImageHeader::ImageMethod method = static_cast<ImageHeader::ImageMethod>(i);
CHECK_EQ(primary_header.GetImageMethod(method), image_header.GetImageMethod(method))
<< method;
}
FileWithRange file_with_range;
{
TimingLogger::ScopedTiming timing("OpenImageFile", logger); // Most likely, the image is compressed and doesn't really need alignment. We enforce page // size alignment just in case the image is uncompressed.
file_with_range = OS::OpenFileDirectlyOrFromZip(
image_filename, OatFile::kZipSeparator, /*alignment=*/MemMap::GetPageSize(), error_msg); if (file_with_range.file == nullptr) { return nullptr;
}
} return Init(file_with_range.file.get(),
file_with_range.start,
file_with_range.length,
image_filename,
image_location, /*profile_files=*/{}, /*allow_direct_mapping=*/true,
logger,
image_reservation,
error_msg);
}
constauto& bitmap_section = image_header.GetImageBitmapSection(); // The location we want to map from is the first aligned page after the end of the stored // (possibly compressed) data. const size_t image_bitmap_offset =
RoundUp(sizeof(ImageHeader) + image_header.GetDataSize(), kElfSegmentAlignment); const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size(); if (end_of_bitmap != image_file_size) {
*error_msg = StringPrintf("Image file size does not equal end of bitmap: size=%zu vs. %zu.",
image_file_size,
end_of_bitmap); return nullptr;
}
// GetImageBegin is the preferred address to map the image. If we manage to map the // image at the image begin, the amount of fixup work required is minimized. // If it is pic we will retry with error_msg for the2 failure case. Pass a null error_msg to // avoid reading proc maps for a mapping failure and slowing everything down. // For the boot image, we have already reserved the memory and we load the image // into the `image_reservation`.
MemMap map = LoadImageFile(image_filename,
image_location,
image_header,
file->Fd(),
start,
allow_direct_mapping,
logger,
image_reservation,
error_msg); if (!map.IsValid()) {
DCHECK(!error_msg->empty()); return nullptr;
}
DCHECK_EQ(0, memcmp(&image_header, map.Begin(), sizeof(ImageHeader)));
MemMap image_bitmap_map = MemMap::MapFile(bitmap_section.Size(),
PROT_READ,
MAP_PRIVATE,
file->Fd(),
start + image_bitmap_offset, /*low_4gb=*/false,
image_filename,
error_msg); if (!image_bitmap_map.IsValid()) {
*error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str()); return nullptr;
} const uint32_t bitmap_index = ImageSpace::bitmap_index_.fetch_add(1);
std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u",
image_filename,
bitmap_index)); // Bitmap only needs to cover until the end of the mirror objects section. const ImageSection& image_objects = image_header.GetObjectsSection(); // We only want the mirror object, not the ArtFields and ArtMethods.
uint8_t* const image_end = map.Begin() + image_objects.End();
accounting::ContinuousSpaceBitmap bitmap;
{
TimingLogger::ScopedTiming timing("CreateImageBitmap", logger);
bitmap = accounting::ContinuousSpaceBitmap::CreateFromMemMap(
bitmap_name,
std::move(image_bitmap_map), reinterpret_cast<uint8_t*>(map.Begin()), // Make sure the bitmap is aligned to card size instead of just bitmap word size.
RoundUp(image_objects.End(), gc::accounting::CardTable::kCardSize)); if (!bitmap.IsValid()) {
*error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str()); return nullptr;
}
} // We only want the mirror object, not the ArtFields and ArtMethods.
std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename,
image_location,
profile_files,
std::move(map),
std::move(bitmap),
image_end)); return space;
}
private: // Remove duplicates found in the `old_set` from the `new_set`. // Record the removed Strings for remapping. No read barriers are needed as the // tables are either just being loaded and not yet a part of the heap, or boot // image intern tables with non-moveable Strings used when loading an app image. staticvoid RemoveDuplicates(const InternTable::UnorderedSet& old_set, /*inout*/InternTable::UnorderedSet* new_set, /*inout*/SafeMap<mirror::String*, mirror::String*>* intern_remap)
REQUIRES_SHARED(Locks::mutator_lock_) { if (old_set.size() < new_set->size()) { for (const GcRoot<mirror::String>& old_s : old_set) { auto new_it = new_set->find(old_s); if (UNLIKELY(new_it != new_set->end())) {
intern_remap->Put(new_it->Read<kWithoutReadBarrier>(), old_s.Read<kWithoutReadBarrier>());
new_set->erase(new_it);
}
}
} else { for (auto new_it = new_set->begin(), end = new_set->end(); new_it != end; ) { auto old_it = old_set.find(*new_it); if (UNLIKELY(old_it != old_set.end())) {
intern_remap->Put(new_it->Read<kWithoutReadBarrier>(),
old_it->Read<kWithoutReadBarrier>());
new_it = new_set->erase(new_it);
} else {
++new_it;
}
}
}
}
staticbool ValidateBootImageChecksum(constchar* image_filename, const ImageHeader& image_header, const OatFile* oat_file,
ArrayRef<ImageSpace* const> boot_image_spaces, /*out*/size_t* boot_image_space_dependencies, /*out*/std::string* error_msg) { // Use the boot image component count to calculate the checksum from // the appropriate number of boot image chunks.
uint32_t boot_image_component_count = image_header.GetBootImageComponentCount();
size_t expected_image_component_count = ImageSpace::GetNumberOfComponents(boot_image_spaces); if (boot_image_component_count > expected_image_component_count) {
*error_msg = StringPrintf("Too many boot image dependencies (%u > %zu) in image %s",
boot_image_component_count,
expected_image_component_count,
image_filename); returnfalse;
}
uint32_t checksum = 0u;
size_t chunk_count = 0u;
size_t space_pos = 0u;
uint64_t boot_image_size = 0u; for (size_t component_count = 0u; component_count != boot_image_component_count; ) { const ImageHeader& current_header = boot_image_spaces[space_pos]->GetImageHeader(); if (current_header.GetComponentCount() > boot_image_component_count - component_count) {
*error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, " "%u is between %zu and %zu",
image_filename,
boot_image_component_count,
component_count,
component_count + current_header.GetComponentCount()); returnfalse;
}
component_count += current_header.GetComponentCount();
checksum ^= current_header.GetImageChecksum();
chunk_count += 1u;
space_pos += current_header.GetImageSpaceCount();
boot_image_size += current_header.GetImageReservationSize();
} if (image_header.GetBootImageChecksum() != checksum) {
*error_msg = StringPrintf("Boot image checksum mismatch (0x%08x != 0x%08x) in image %s",
image_header.GetBootImageChecksum(),
checksum,
image_filename); returnfalse;
} if (image_header.GetBootImageSize() != boot_image_size) {
*error_msg = StringPrintf("Boot image size mismatch (0x%08x != 0x%08" PRIx64 ") in image %s",
image_header.GetBootImageSize(),
boot_image_size,
image_filename); returnfalse;
} // Oat checksums, if present, have already been validated, so we know that // they match the loaded image spaces. Therefore, we just verify that they // are consistent in the number of boot image chunks they list by looking // for the kImageChecksumPrefix at the start of each component. constchar* oat_boot_class_path_checksums =
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey); if (oat_boot_class_path_checksums != nullptr) {
size_t oat_bcp_chunk_count = 0u; while (*oat_boot_class_path_checksums == kImageChecksumPrefix) {
oat_bcp_chunk_count += 1u; // Find the start of the next component if any. constchar* separator = strchr(oat_boot_class_path_checksums, ':');
oat_boot_class_path_checksums = (separator != nullptr) ? separator + 1u : "";
} if (oat_bcp_chunk_count != chunk_count) {
*error_msg = StringPrintf("Boot image chunk count mismatch (%zu != %zu) in image %s",
oat_bcp_chunk_count,
chunk_count,
image_filename); returnfalse;
}
}
*boot_image_space_dependencies = space_pos; returntrue;
}
// The runtime might not be available at this point if we're running dex2oat or oatdump, in // which case we just truncate the madvise optimization limit completely.
Runtime* runtime = Runtime::Current(); const size_t madvise_size_limit = runtime ? runtime->GetMadviseWillNeedSizeArt() : 0;
constbool is_compressed = image_header.HasCompressedBlock(); if (!is_compressed && allow_direct_mapping) {
uint8_t* address = (image_reservation != nullptr) ? image_reservation->Begin() : nullptr; // The reserved memory size is aligned up to kElfSegmentAlignment to ensure // that the next reserved area will be aligned to the value.
MemMap map = MemMap::MapFileAtAddress(
address,
CondRoundUp<kPageSizeAgnostic>(image_header.GetImageSize(), kElfSegmentAlignment),
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
start, /*low_4gb=*/true,
image_filename, /*reuse=*/false,
image_reservation,
error_msg); if (map.IsValid()) {
Runtime::MadviseFileForRange(
madvise_size_limit, map.Size(), map.Begin(), map.End(), image_filename);
} return map;
}
// Reserve output and copy/decompress into it. // The reserved memory size is aligned up to kElfSegmentAlignment to ensure // that the next reserved area will be aligned to the value.
MemMap map = MemMap::MapAnonymous(image_location,
CondRoundUp<kPageSizeAgnostic>(image_header.GetImageSize(),
kElfSegmentAlignment),
PROT_READ | PROT_WRITE, /*low_4gb=*/ true,
image_reservation,
error_msg); if (map.IsValid()) { const size_t stored_size = image_header.GetDataSize();
MemMap temp_map = MemMap::MapFile(sizeof(ImageHeader) + stored_size,
PROT_READ,
MAP_PRIVATE,
fd,
start, /*low_4gb=*/false,
image_filename,
error_msg); if (!temp_map.IsValid()) {
DCHECK(error_msg == nullptr || !error_msg->empty()); return MemMap::Invalid();
}
if (is_compressed) {
memcpy(map.Begin(), &image_header, sizeof(ImageHeader));
Runtime::ScopedThreadPoolUsage stpu;
ThreadPool* const pool = stpu.GetThreadPool(); const uint64_t start_time = NanoTime();
Thread* const self = Thread::Current(); static constexpr size_t kMinBlocks = 2u; constbool use_parallel = pool != nullptr && image_header.GetBlockCount() >= kMinBlocks; bool failed_decompression = false; for (const ImageHeader::Block& block : image_header.GetBlocks(temp_map.Begin())) { auto function = [&](Thread*) { const uint64_t start2 = NanoTime();
ScopedTrace trace("LZ4 decompress block"); bool result = block.Decompress(/*out_ptr=*/map.Begin(), /*in_ptr=*/temp_map.Begin(),
error_msg); if (!result) {
failed_decompression = true; if (error_msg != nullptr) {
*error_msg = "Failed to decompress image block " + *error_msg;
}
}
VLOG(image) << "Decompress block " << block.GetDataSize() << " -> "
<< block.GetImageSize() << " in " << PrettyDuration(NanoTime() - start2);
}; if (use_parallel) {
pool->AddTask(self, new FunctionTask(std::move(function)));
} else {
function(self);
}
} if (use_parallel) {
ScopedTrace trace("Waiting for workers");
pool->Wait(self, true, false);
} const uint64_t time = NanoTime() - start_time; // Add one 1 ns to prevent possible divide by 0.
VLOG(image) << "Decompressing image took " << PrettyDuration(time) << " ("
<< PrettySize(static_cast<uint64_t>(map.Size()) * MsToNs(1000) / (time + 1))
<< "/s)"; if (failed_decompression) {
DCHECK(error_msg == nullptr || !error_msg->empty()); return MemMap::Invalid();
}
} else {
DCHECK(!allow_direct_mapping); // We do not allow direct mapping for boot image extensions compiled to a memfd. // This prevents wasting memory by kernel keeping the contents of the file alive // despite these contents being unreachable once the file descriptor is closed // and mmapped memory is copied for all existing mappings. // // Most pages would be copied during relocation while there is only one mapping. // We could use MAP_SHARED for relocation and then msync() and remap MAP_PRIVATE // as required for forking from zygote, but there would still be some pages // wasted anyway and we want to avoid that. (For example, static synchronized // methods use the class object for locking and thus modify its lockword.)
// No other process should race to overwrite the extension in memfd.
DCHECK_EQ(memcmp(temp_map.Begin(), &image_header, sizeof(ImageHeader)), 0);
memcpy(map.Begin(), temp_map.Begin(), temp_map.Size());
}
}
bool ImageSpace::BootImageLayout::MatchNamedComponents(
ArrayRef<const std::string> named_components, /*out*/std::vector<NamedComponentLocation>* named_component_locations, /*out*/std::string* error_msg) {
DCHECK(!named_components.empty());
DCHECK(named_component_locations->empty());
named_component_locations->reserve(named_components.size());
size_t bcp_component_count = boot_class_path_.size();
size_t bcp_pos = 0;
std::string base_name; for (size_t i = 0, size = named_components.size(); i != size; ++i) {
std::string component = named_components[i];
std::vector<std::string> profile_filenames; // Empty.
std::vector<std::string> parts = android::base::Split(component, {kProfileSeparator}); for (size_t j = 0; j < parts.size(); j++) { if (j == 0) {
component = std::move(parts[j]);
DCHECK(!component.empty()); // Checked by VerifyImageLocation()
} else {
profile_filenames.push_back(std::move(parts[j]));
DCHECK(!profile_filenames.back().empty()); // Checked by VerifyImageLocation()
}
}
size_t slash_pos = component.rfind('/');
std::string base_location; if (i == 0u) { // The primary boot image name is taken as provided. It forms the base // for expanding the extension filenames. if (slash_pos != std::string::npos) {
base_name = component.substr(slash_pos + 1u);
base_location = component;
} else {
base_name = component;
base_location = GetBcpComponentPath(0u) + component;
}
} else {
std::string to_match; if (slash_pos != std::string::npos) { // If we have the full path, we just need to match the filename to the BCP component.
base_location = component.substr(0u, slash_pos + 1u) + base_name;
to_match = component;
} while (true) { if (slash_pos == std::string::npos) { // If we do not have a full path, we need to update the path based on the BCP location.
std::string path = GetBcpComponentPath(bcp_pos);
to_match = path + component;
base_location = path + base_name;
} if (ExpandLocation(base_location, bcp_pos) == to_match) { break;
}
++bcp_pos; if (bcp_pos == bcp_component_count) {
*error_msg = StringPrintf("Image component %s does not match a boot class path component",
component.c_str()); returnfalse;
}
}
} for (std::string& profile_filename : profile_filenames) { if (profile_filename.find('/') == std::string::npos) {
profile_filename.insert(/*pos*/ 0u, GetBcpComponentPath(bcp_pos));
}
}
NamedComponentLocation location;
location.base_location = base_location;
location.bcp_index = bcp_pos;
location.profile_filenames = profile_filenames;
named_component_locations->push_back(location);
++bcp_pos;
} returntrue;
}
bool ImageSpace::BootImageLayout::ValidateBootImageChecksum(constchar* file_description, const ImageHeader& header, /*out*/std::string* error_msg) {
uint32_t boot_image_component_count = header.GetBootImageComponentCount(); if (chunks_.empty() != (boot_image_component_count == 0u)) {
*error_msg = StringPrintf("Unexpected boot image component count in %s: %u, %s",
file_description,
boot_image_component_count,
chunks_.empty() ? "should be 0" : "should not be 0"); returnfalse;
}
uint32_t component_count = 0u;
uint32_t composite_checksum = 0u;
uint64_t boot_image_size = 0u; for (const ImageChunk& chunk : chunks_) { if (component_count == boot_image_component_count) { break; // Hit the component count.
} if (chunk.start_index != component_count) { break; // End of contiguous chunks, fail below; same as reaching end of `chunks_`.
} if (chunk.component_count > boot_image_component_count - component_count) {
*error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, " "%u is between %u and %u",
file_description,
boot_image_component_count,
component_count,
component_count + chunk.component_count); returnfalse;
}
component_count += chunk.component_count;
composite_checksum ^= chunk.checksum;
boot_image_size += chunk.reservation_size;
}
DCHECK_LE(component_count, boot_image_component_count); if (component_count != boot_image_component_count) {
*error_msg = StringPrintf("Missing boot image components for checksum in %s: %u > %u",
file_description,
boot_image_component_count,
component_count); returnfalse;
} if (composite_checksum != header.GetBootImageChecksum()) {
*error_msg = StringPrintf("Boot image checksum mismatch in %s: 0x%08x != 0x%08x",
file_description,
header.GetBootImageChecksum(),
composite_checksum); returnfalse;
} if (boot_image_size != header.GetBootImageSize()) {
*error_msg = StringPrintf("Boot image size mismatch in %s: 0x%08x != 0x%08" PRIx64,
file_description,
header.GetBootImageSize(),
boot_image_size); returnfalse;
} returntrue;
}
std::string actual_filename = ExpandLocation(base_filename, bcp_index); int bcp_image_fd = bcp_index < boot_class_path_image_files_.size() ?
boot_class_path_image_files_[bcp_index].Fd() :
-1;
ImageHeader header; // When BCP image is provided as FD, it needs to be dup'ed (since it's stored in unique_fd) so // that it can later be used in LoadComponents. auto image_file = bcp_image_fd >= 0
? std::make_unique<File>(DupCloexec(bcp_image_fd), actual_filename, /*check_usage=*/ false)
: std::unique_ptr<File>(OS::OpenFileForReading(actual_filename.c_str())); if (!image_file || !image_file->IsOpened()) {
*error_msg = StringPrintf("Unable to open file \"%s\" for reading image header",
actual_filename.c_str()); returnfalse;
} if (!ReadSpecificImageHeader(image_file.get(), actual_filename.c_str(), &header, error_msg)) { returnfalse;
} constchar* file_description = actual_filename.c_str(); if (!ValidateHeader(header, bcp_index, file_description, error_msg)) { returnfalse;
}
// Validate oat files. We do it here so that the boot image will be re-compiled in memory if it's // outdated.
size_t component_count = (header.GetImageSpaceCount() == 1u) ? header.GetComponentCount() : 1u; for (size_t i = 0; i < header.GetImageSpaceCount(); i++) { if (!ValidateOatFile(base_location, base_filename, bcp_index + i, component_count, error_msg)) { returnfalse;
}
}
bool ImageSpace::BootImageLayout::CompileBootclasspathElements( const std::string& base_location, const std::string& base_filename,
size_t bcp_index, const std::vector<std::string>& profile_filenames,
ArrayRef<const std::string> dependencies, /*out*/std::string* error_msg) {
DCHECK_LE(total_component_count_, next_bcp_index_);
DCHECK_LE(next_bcp_index_, bcp_index);
size_t bcp_component_count = boot_class_path_.size();
DCHECK_LT(bcp_index, bcp_component_count);
DCHECK(!profile_filenames.empty()); if (total_component_count_ != bcp_index) { // We require all previous BCP components to have a boot image space (primary or extension).
*error_msg = "Cannot compile extension because of missing dependencies."; returnfalse;
}
Runtime* runtime = Runtime::Current(); if (!runtime->IsImageDex2OatEnabled()) {
*error_msg = "Cannot compile bootclasspath because dex2oat for image compilation is disabled."; returnfalse;
}
// Check dependencies.
DCHECK_EQ(dependencies.empty(), bcp_index == 0);
size_t dependency_component_count = 0; for (size_t i = 0, size = dependencies.size(); i != size; ++i) { if (chunks_.size() == i || chunks_[i].start_index != dependency_component_count) {
*error_msg = StringPrintf("Missing extension dependency \"%s\"", dependencies[i].c_str()); returnfalse;
}
dependency_component_count += chunks_[i].component_count;
}
// Collect locations from the profile.
std::set<std::string> dex_locations; for (const std::string& profile_filename : profile_filenames) {
std::unique_ptr<File> profile_file(OS::OpenFileForReading(profile_filename.c_str())); if (profile_file == nullptr) {
*error_msg = StringPrintf("Failed to open profile file \"%s\" for reading, error: %s",
profile_filename.c_str(),
strerror(errno)); returnfalse;
}
// TODO: Rewrite ProfileCompilationInfo to provide a better interface and // to store the dex locations in uncompressed section of the file. auto collect_fn = [&dex_locations](const std::string& dex_location,
[[maybe_unused]] uint32_t checksum) {
dex_locations.insert(dex_location); // Just collect locations. returnfalse; // Do not read the profile data.
};
ProfileCompilationInfo info(/*for_boot_image=*/ true); if (!info.Load(profile_file->Fd(), /*merge_classes=*/ true, collect_fn)) {
*error_msg = StringPrintf("Failed to scan profile from %s", profile_filename.c_str()); returnfalse;
}
}
// Match boot class path components to locations from profile. // Note that the profile records only filenames without paths.
size_t bcp_end = bcp_index; for (; bcp_end != bcp_component_count; ++bcp_end) { const std::string& bcp_component = boot_class_path_locations_[bcp_end];
size_t slash_pos = bcp_component.rfind('/');
DCHECK_NE(slash_pos, std::string::npos);
std::string bcp_component_name = bcp_component.substr(slash_pos + 1u); if (dex_locations.count(bcp_component_name) == 0u) { break; // Did not find the current location in dex file.
}
}
if (bcp_end == bcp_index) { // No data for the first (requested) component.
*error_msg = StringPrintf("The profile does not contain data for %s",
boot_class_path_locations_[bcp_index].c_str()); returnfalse;
}
// We currently cannot guarantee that the boot class path has no verification failures. // And we do not want to compile anything, compilation should be done by JIT in zygote.
args.push_back("--compiler-filter=verify");
// Pass the profiles. for (const std::string& profile_filename : profile_filenames) {
args.push_back("--profile-file=" + profile_filename);
}
// Do not let the file descriptor numbers change the compilation output.
args.push_back("--avoid-storing-invocation");
if (!kIsTargetBuild) {
args.push_back("--host");
}
// Image compiler options go last to allow overriding above args, such as --compiler-filter. for (const std::string& compiler_option : runtime->GetImageCompilerOptions()) {
args.push_back(compiler_option);
}
// Compile.
VLOG(image) << "Compiling boot bootclasspath for " << (bcp_end - bcp_index)
<< " components, starting from " << boot_class_path_locations_[bcp_index]; if (!Exec(args, error_msg)) { returnfalse;
}
// Read and validate the image header.
ImageHeader header;
{
File image_file(art_fd.release(), /*check_usage=*/ false); if (!ReadSpecificImageHeader(&image_file, "compiled image file", &header, error_msg)) { returnfalse;
}
art_fd.reset(image_file.Release());
} constchar* file_description = "compiled image file"; if (!ValidateHeader(header, bcp_index, file_description, error_msg)) { returnfalse;
}
std::vector<NamedComponentLocation> named_component_locations; if (!MatchNamedComponents(named_components, &named_component_locations, error_msg)) { returnfalse;
}
// Load the image headers of named components.
DCHECK_EQ(named_component_locations.size(), named_components.size()); const size_t bcp_component_count = boot_class_path_.size();
size_t bcp_pos = 0u; for (size_t i = 0, size = named_components.size(); i != size; ++i) { const std::string& base_location = named_component_locations[i].base_location;
size_t bcp_index = named_component_locations[i].bcp_index; const std::vector<std::string>& profile_filenames =
named_component_locations[i].profile_filenames;
DCHECK_EQ(i == 0, bcp_index == 0); if (bcp_index < bcp_pos) {
DCHECK_NE(i, 0u);
LOG(ERROR) << "Named image component already covered by previous image: " << base_location; continue;
}
std::string local_error_msg;
std::string base_filename; if (!filename_fn(base_location, &base_filename, &local_error_msg) ||
!ReadHeader(base_location, base_filename, bcp_index, &local_error_msg)) {
LOG(ERROR) << "Error reading named image component header for " << base_location
<< ", error: " << local_error_msg; // If the primary boot image is invalid, we generate a single full image. This is faster than // generating the primary boot image and the extension separately. if (bcp_index == 0) { if (!allow_in_memory_compilation) { // The boot image is unusable and we can't continue by generating a boot image in memory. // All we can do is to return.
*error_msg = std::move(local_error_msg); returnfalse;
} // We must at least have profiles for the core libraries. if (profile_filenames.empty()) {
*error_msg = "Full boot image cannot be compiled because no profile is provided."; returnfalse;
}
std::vector<std::string> all_profiles; for (const NamedComponentLocation& named_component_location : named_component_locations) { const std::vector<std::string>& profiles = named_component_location.profile_filenames;
all_profiles.insert(all_profiles.end(), profiles.begin(), profiles.end());
} if (!CompileBootclasspathElements(base_location,
base_filename, /*bcp_index=*/ 0,
all_profiles, /*dependencies=*/ ArrayRef<const std::string>{},
&local_error_msg)) {
*error_msg =
StringPrintf("Full boot image cannot be compiled: %s", local_error_msg.c_str()); returnfalse;
} // No extensions are needed. returntrue;
} bool should_compile_extension = allow_in_memory_compilation && !profile_filenames.empty(); if (!should_compile_extension ||
!CompileBootclasspathElements(base_location,
base_filename,
bcp_index,
profile_filenames,
components.SubArray(/*pos=*/ 0, /*length=*/ 1),
&local_error_msg)) { if (should_compile_extension) {
LOG(ERROR) << "Error compiling boot image extension for " << boot_class_path_[bcp_index]
<< ", error: " << local_error_msg;
}
bcp_pos = bcp_index + 1u; // Skip at least this component.
DCHECK_GT(bcp_pos, GetNextBcpIndex()); continue;
}
}
bcp_pos = GetNextBcpIndex();
}
// Look for remaining components if there are any wildcard specifications.
ArrayRef<const std::string> search_paths = components.SubArray(/*pos=*/ named_components_count); if (!search_paths.empty()) { const std::string& primary_base_location = named_component_locations[0].base_location;
size_t base_slash_pos = primary_base_location.rfind('/');
DCHECK_NE(base_slash_pos, std::string::npos);
std::string base_name = primary_base_location.substr(base_slash_pos + 1u);
DCHECK(!base_name.empty()); while (bcp_pos != bcp_component_count) { const std::string& bcp_component = boot_class_path_[bcp_pos]; bool found = false; for (const std::string& path : search_paths) {
std::string base_location; if (path.size() == 1u) {
DCHECK_EQ(path, "*");
size_t slash_pos = bcp_component.rfind('/');
DCHECK_NE(slash_pos, std::string::npos);
base_location = bcp_component.substr(0u, slash_pos + 1u) + base_name;
} else {
DCHECK(path.ends_with("/*"));
base_location = path.substr(0u, path.size() - 1u) + base_name;
}
std::string err_msg; // Ignored.
std::string base_filename; if (filename_fn(base_location, &base_filename, &err_msg) &&
ReadHeader(base_location, base_filename, bcp_pos, &err_msg)) {
VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
bcp_pos = GetNextBcpIndex();
found = true; break;
}
} if (!found) {
++bcp_pos;
}
}
}
DCHECK_LE(image_reservation_size, kMaxTotalImageReservationSize);
static_assert(kMaxTotalImageReservationSize < std::numeric_limits<uint32_t>::max()); if (extra_reservation_size > std::numeric_limits<uint32_t>::max() - image_reservation_size) { // Since the `image_reservation_size` is limited to kMaxTotalImageReservationSize, // the `extra_reservation_size` would have to be really excessive to fail this check.
*error_msg = StringPrintf("Excessive extra reservation size: %zu", extra_reservation_size); returnfalse;
}
// Reserve address space. If relocating, choose a random address for ALSR.
uint8_t* addr = reinterpret_cast<uint8_t*>(
relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : base_address);
MemMap image_reservation =
ReserveBootImageMemory(addr, image_reservation_size + extra_reservation_size, error_msg); if (!image_reservation.IsValid()) { returnfalse;
}
// Load components.
std::vector<std::unique_ptr<ImageSpace>> spaces;
spaces.reserve(image_component_count);
size_t max_image_space_dependencies = 0u; for (size_t i = 0, num_chunks = chunks.size(); i != num_chunks; ++i) { const BootImageLayout::ImageChunk& chunk = chunks[i];
std::string extension_error_msg;
uint8_t* old_reservation_begin = image_reservation.Begin();
size_t old_reservation_size = image_reservation.Size();
DCHECK_LE(chunk.reservation_size, old_reservation_size); if (!LoadComponents(chunk,
validate_oat_file,
max_image_space_dependencies,
logger,
&spaces,
&image_reservation,
(i == 0) ? error_msg : &extension_error_msg)) { // Failed to load the chunk. If this is the primary boot image, report the error. if (i == 0) { returnfalse;
} // For extension, shrink the reservation (and remap if needed, see below).
size_t new_reservation_size = old_reservation_size - chunk.reservation_size; if (new_reservation_size == 0u) {
DCHECK_EQ(extra_reservation_size, 0u);
DCHECK_EQ(i + 1u, num_chunks);
image_reservation.Reset();
} elseif (old_reservation_begin != image_reservation.Begin()) { // Part of the image reservation has been used and then unmapped when // rollling back the partial boot image extension load. Try to remap // the image reservation. As this should be running single-threaded, // the address range should still be available to mmap().
image_reservation.Reset();
std::string remap_error_msg;
image_reservation = ReserveBootImageMemory(old_reservation_begin,
new_reservation_size,
&remap_error_msg); if (!image_reservation.IsValid()) {
*error_msg = StringPrintf("Failed to remap boot image reservation after failing " "to load boot image extension (%s: %s): %s",
boot_class_path_locations_[chunk.start_index].c_str(),
extension_error_msg.c_str(),
remap_error_msg.c_str()); returnfalse;
}
} else {
DCHECK_EQ(old_reservation_size, image_reservation.Size());
image_reservation.SetSize(new_reservation_size);
}
LOG(ERROR) << "Failed to load boot image extension "
<< boot_class_path_locations_[chunk.start_index] << ": " << extension_error_msg;
} // Update `max_image_space_dependencies` if all previous BCP components // were covered and loading the current chunk succeeded.
size_t total_component_count = 0; for (const std::unique_ptr<ImageSpace>& space : spaces) {
total_component_count += space->GetComponentCount();
} if (max_image_space_dependencies == chunk.start_index &&
total_component_count == chunk.start_index + chunk.component_count) {
max_image_space_dependencies = chunk.start_index + chunk.component_count;
}
}
MemMap local_extra_reservation; if (!RemapExtraReservation(extra_reservation_size,
&image_reservation,
&local_extra_reservation,
error_msg)) { returnfalse;
}
MaybeRelocateSpaces(spaces, logger);
if (executable_) { constvoid* boot_image_begin = spaces.front()->Begin();
ArtMethod* resolution_method =
spaces.front()->GetImageHeader().GetImageMethod(ImageHeader::kResolutionMethod); for (const std::unique_ptr<ImageSpace>& space : spaces) {
DCHECK(space->oat_file_non_owned_->IsExecutable());
space->oat_file_non_owned_->InitializeRelocations(resolution_method, boot_image_begin);
}
}
void DeduplicateInternedStrings(ArrayRef<const std::unique_ptr<ImageSpace>> spaces,
TimingLogger* logger) { // Note: These `spaces` are not yet part of the heap, so we do not need the mutator lock. // Pretend to acquire the reader access for static analysis.
FakeReaderMutexLock fake_lock(*Locks::mutator_lock_);
TimingLogger::ScopedTiming timing("DeduplicateInternedStrings", logger);
DCHECK(!spaces.empty());
size_t num_spaces = spaces.size(); const ImageHeader& primary_header = spaces.front()->GetImageHeader();
size_t primary_image_count = primary_header.GetImageSpaceCount();
size_t primary_image_component_count = primary_header.GetComponentCount();
DCHECK_LE(primary_image_count, num_spaces); // The primary boot image can be generated with `--single-image` on device, when generated // in-memory or with odrefresh.
DCHECK(primary_image_count == primary_image_component_count || primary_image_count == 1);
size_t component_count = primary_image_component_count;
size_t space_pos = primary_image_count; while (space_pos != num_spaces) { const ImageHeader& current_header = spaces[space_pos]->GetImageHeader();
size_t image_space_count = current_header.GetImageSpaceCount();
DCHECK_LE(image_space_count, num_spaces - space_pos);
size_t dependency_component_count = current_header.GetBootImageComponentCount();
DCHECK_LE(dependency_component_count, component_count); if (dependency_component_count < component_count) { // There shall be no duplicate strings with the components that this space depends on. // Find the end of the dependencies, i.e. start of non-dependency images.
size_t start_component_count = primary_image_component_count;
size_t start_pos = primary_image_count; while (start_component_count != dependency_component_count) { const ImageHeader& dependency_header = spaces[start_pos]->GetImageHeader();
DCHECK_LE(dependency_header.GetComponentCount(),
dependency_component_count - start_component_count);
start_component_count += dependency_header.GetComponentCount();
start_pos += dependency_header.GetImageSpaceCount();
} // Remove duplicates from all intern tables belonging to the chunk.
ArrayRef<const std::unique_ptr<ImageSpace>> old_spaces =
spaces.SubArray(/*pos=*/ start_pos, space_pos - start_pos);
SafeMap<mirror::String*, mirror::String*> intern_remap; for (size_t i = 0; i != image_space_count; ++i) {
ImageSpace* new_space = spaces[space_pos + i].get();
Loader::RemoveInternTableDuplicates(old_spaces, new_space, &intern_remap);
} // Remap string for all spaces belonging to the chunk. if (!intern_remap.empty()) { for (size_t i = 0; i != image_space_count; ++i) {
ImageSpace* new_space = spaces[space_pos + i].get();
Loader::RemapInternedStringDuplicates(intern_remap, new_space);
}
}
}
component_count += current_header.GetComponentCount();
space_pos += image_space_count;
}
}
File image_file(art_fd.release(), image_filename, /*check_usage=*/false);
int64_t file_length = image_file.GetLength(); if (file_length < 0) {
*error_msg =
ART_FORMAT("Failed to get file length of '{}': {}", image_filename, strerror(errno)); return nullptr;
}
std::unique_ptr<ImageSpace> result = Loader::Init(&image_file, /*start=*/0,
file_length,
image_filename.c_str(),
image_location.c_str(),
profile_files, /*allow_direct_mapping=*/false,
logger,
image_reservation,
error_msg); // Note: We're closing the image file descriptor here when we destroy // the `image_file` as we no longer need it. return result;
}
// If we are in /system we can assume the image is good. We can also // assume this if we are using a relocated image (i.e. image checksum // matches) since this is only different by the offset. We need this to // make sure that host tests continue to work. // Since we are the boot image, pass null since we load the oat file from the boot image oat // file name. return Loader::Init(image_filename.c_str(),
image_location.c_str(),
logger,
image_reservation,
error_msg);
}
bool OpenOatFile(ImageSpace* space,
android::base::unique_fd vdex_fd,
android::base::unique_fd oat_fd,
ArrayRef<const std::string> dex_filenames,
ArrayRef<File> dex_files, bool validate_oat_file,
ArrayRef<const std::unique_ptr<ImageSpace>> dependencies,
TimingLogger* logger, /*inout*/ MemMap* image_reservation, /*out*/ std::string* error_msg) { // VerifyImageAllocations() will be called later in Runtime::Init() // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_ // and ArtField::java_lang_reflect_ArtField_, which are used from // Object::SizeOf() which VerifyImageAllocations() calls, are not // set yet at this point.
DCHECK(image_reservation != nullptr);
std::unique_ptr<OatFile> oat_file;
{
TimingLogger::ScopedTiming timing("OpenOatFile", logger);
std::string oat_filename =
ImageHeader::GetOatLocationFromImageLocation(space->GetImageFilename());
DCHECK_EQ(vdex_fd.get() != -1, oat_fd.get() != -1); if (vdex_fd.get() == -1) {
oat_file.reset(OatFile::Open(/*zip_fd=*/-1,
oat_filename,
oat_filename,
executable_, /*low_4gb=*/false,
dex_filenames,
dex_files,
image_reservation,
error_msg));
} else {
oat_file.reset(OatFile::Open(/*zip_fd=*/-1,
vdex_fd.get(),
oat_fd.get(),
oat_filename,
executable_, /*low_4gb=*/false,
dex_filenames,
dex_files,
image_reservation,
error_msg)); // We no longer need the file descriptors and they will be closed by // the unique_fd destructor when we leave this function.
}
if (oat_file == nullptr) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
oat_filename.c_str(),
space->GetName(),
error_msg->c_str()); returnfalse;
} const ImageHeader& image_header = space->GetImageHeader();
uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
uint32_t image_oat_checksum = image_header.GetOatChecksum(); if (oat_checksum != image_oat_checksum) {
*error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum" " 0x%x in image %s",
oat_checksum,
image_oat_checksum,
space->GetName()); returnfalse;
} constchar* oat_boot_class_path =
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
oat_boot_class_path = (oat_boot_class_path != nullptr) ? oat_boot_class_path : ""; constchar* oat_boot_class_path_checksums =
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
oat_boot_class_path_checksums =
(oat_boot_class_path_checksums != nullptr) ? oat_boot_class_path_checksums : "";
size_t component_count = image_header.GetComponentCount(); if (component_count == 0u) { if (oat_boot_class_path[0] != 0 || oat_boot_class_path_checksums[0] != 0) {
*error_msg = StringPrintf("Unexpected non-empty boot class path %s and/or checksums %s" " in image %s",
oat_boot_class_path,
oat_boot_class_path_checksums,
space->GetName()); returnfalse;
}
} elseif (dependencies.empty()) {
std::string expected_boot_class_path = Join(ArrayRef<const std::string>(
boot_class_path_locations_).SubArray(0u, component_count), ':'); if (expected_boot_class_path != oat_boot_class_path) {
*error_msg = StringPrintf("Failed to match oat boot class path %s to expected " "boot class path %s in image %s",
oat_boot_class_path,
expected_boot_class_path.c_str(),
space->GetName()); returnfalse;
}
} else {
std::string local_error_msg; if (!VerifyBootClassPathChecksums(
oat_boot_class_path_checksums,
oat_boot_class_path,
dependencies,
ArrayRef<const std::string>(boot_class_path_locations_),
ArrayRef<const std::string>(boot_class_path_),
&local_error_msg)) {
*error_msg = StringPrintf("Failed to verify BCP %s with checksums %s in image %s: %s",
oat_boot_class_path,
oat_boot_class_path_checksums,
space->GetName(),
local_error_msg.c_str()); returnfalse;
}
}
ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin();
CHECK(image_header.GetOatDataBegin() != nullptr);
uint8_t* oat_data_begin = image_header.GetOatDataBegin() + relocation_diff; if (oat_file->Begin() != oat_data_begin) {
*error_msg = StringPrintf("Oat file '%s' referenced from image %s has unexpected begin" " %p v. %p",
oat_filename.c_str(),
space->GetName(),
oat_file->Begin(),
oat_data_begin); returnfalse;
}
} if (validate_oat_file) {
TimingLogger::ScopedTiming timing("ValidateOatFile", logger); if (!ImageSpace::ValidateOatFile(*oat_file, error_msg)) {
DCHECK(!error_msg->empty()); returnfalse;
}
}
// As an optimization, madvise the oat file into memory if it's being used // for execution with an active runtime. This can significantly improve // ZygoteInit class preload performance. if (executable_) {
Runtime* runtime = Runtime::Current(); if (runtime != nullptr) {
Runtime::MadviseFileForRange(runtime->GetMadviseWillNeedSizeOdex(),
oat_file->Size(),
oat_file->Begin(),
oat_file->End(),
oat_file->GetLocation());
}
}
// Load the image. We don't validate oat files in this stage because they have been validated // before. if (!LoadImage(layout, /*validate_oat_file=*/ false,
extra_reservation_size,
&logger,
boot_image_spaces,
extra_reservation,
error_msg)) { returnfalse;
}
std::string error_msg; if (loader.LoadFromSystem(extra_reservation_size,
allow_in_memory_compilation,
boot_image_spaces,
extra_reservation,
&error_msg)) { returntrue;
}
LOG(ERROR) << "Could not create image space with image file '"
<< Join(image_locations, kComponentSeparator)
<< "'. Attempting to fall back to imageless running. Error was: "
<< error_msg;
returnfalse;
}
ImageSpace::~ImageSpace() { // Everything done by member destructors. Classes forward-declared in header are now defined.
}
std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(constchar* image, const OatFile* oat_file,
std::string* error_msg) { // Note: The oat file has already been validated. const std::vector<ImageSpace*>& boot_image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces(); return CreateFromAppImage(image,
oat_file,
ArrayRef<ImageSpace* const>(boot_image_spaces),
error_msg);
}
bool ImageSpace::OpenAndSetDexFiles(std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
std::string* error_msg) const { // Note: This `ImageSpace` is not yet part of the heap, so we do not need the mutator lock. // Pretend to acquire the reader access for static analysis of calls to helper functions we need.
FakeReaderMutexLock fake_lock(*Locks::mutator_lock_);
ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
DCHECK(out_dex_files != nullptr); const ImageHeader& header = GetImageHeader();
ObjPtr<mirror::Object> dex_caches_object =
header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches);
DCHECK(dex_caches_object != nullptr);
ObjPtr<mirror::ObjectArray<mirror::DexCache>> dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>(); const OatFile* oat_file = GetOatFile();
uint32_t num_dex_files = oat_file->GetOatHeader().GetDexFileCount(); if (num_dex_files != static_cast<uint32_t>(dex_caches->GetLength())) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from image"; returnfalse;
}
for (uint32_t i = 0; i != num_dex_files; ++i) {
ObjPtr<mirror::DexCache> dex_cache =
dex_caches->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(i);
std::string dex_file_location =
dex_cache->GetLocation<kVerifyNone, kWithoutReadBarrier>()->ToModifiedUtf8(); // At this point, the location in the dex cache (from `--dex-location` passed to dex2oat) is // not necessarily the actual dex location on device. `OpenOatDexFile` uses the table // `OatFile::oat_dex_files_` to find the dex file. For each dex file, the table contains two // keys corresponding to it, one from the oat header (from `--dex-location` passed to dex2oat) // and the other being the actual dex location on device, unless they are the same. The lookup // is based on the former key. When adding the image space, `ClassLinker` will replace the // location in the dex cache with the actual dex location, which is the latter key in the table.
std::unique_ptr<const DexFile> dex_file =
oat_file->OpenOatDexFile(dex_file_location.c_str(), error_msg); if (dex_file == nullptr) { returnfalse;
}
DCHECK(dex_cache->GetDexFile() == nullptr);
dex_cache->SetDexFile(dex_file.get());
out_dex_files->push_back(std::move(dex_file));
} returntrue;
}
bool ImageSpace::ValidateApexVersions(const OatFile& oat_file,
std::string_view runtime_apex_versions,
std::string* error_msg) { // For a boot image, the key value store only exists in the first OAT file. Skip other OAT files. if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) { returntrue;
}
std::optional<std::string_view> oat_apex_versions = oat_file.GetApexVersions(); if (!oat_apex_versions.has_value()) {
*error_msg = StringPrintf("ValidateApexVersions failed to get APEX versions from oat file '%s'",
oat_file.GetLocation().c_str()); returnfalse;
}
bool ImageSpace::ValidateApexVersions(std::string_view oat_apex_versions,
std::string_view runtime_apex_versions, const std::string& file_location,
std::string* error_msg) { // For a boot image, it can be generated from a subset of the bootclasspath. // For an app image, some dex files get compiled with a subset of the bootclasspath. // For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions. if (!runtime_apex_versions.starts_with(oat_apex_versions)) {
*error_msg = ART_FORMAT( "ValidateApexVersions found APEX versions mismatch between oat file '{}' and the runtime " "(Oat file: '{}', Runtime: '{}')",
file_location,
oat_apex_versions,
runtime_apex_versions); returnfalse;
} returntrue;
}
// For a boot image, the key value store only exists in the first OAT file. Skip other OAT files. if (oat_file.GetOatHeader().GetKeyValueStoreSize() != 0) { if (oat_file.GetOatHeader().IsConcurrentCopying() != gUseReadBarrier) {
*error_msg = ART_FORMAT( "ValidateOatFile found read barrier state mismatch (oat file: {}, runtime: {})",
oat_file.GetOatHeader().IsConcurrentCopying(),
gUseReadBarrier); returnfalse;
}
if (!oat_checksums.starts_with(":")) { // Check that we've reached the end of checksums and BCP. if (!oat_checksums.empty()) {
*error_msg = StringPrintf("Expected ':' separator or end of checksums, remaining %s.",
std::string(oat_checksums).c_str()); returnfalse;
} if (bcp_pos != oat_bcp_size) {
*error_msg = StringPrintf("Component count mismatch between checksums (%zu) and BCP (%zu)",
bcp_pos,
oat_bcp_size); returnfalse;
} returntrue;
}
oat_checksums.remove_prefix(1u);
}
// We do not allow dependencies of extensions on dex files. That would require // interleaving the loading of the images with opening the other BCP dex files. returnfalse;
}
// Find the path.
size_t last_slash = image_location.rfind('/');
CHECK_NE(last_slash, std::string::npos);
// We also need to honor path components that were encoded through '@'. Otherwise the loading // code won't be able to find the images. if (image_location.find('@', last_slash) != std::string::npos) {
last_slash = image_location.rfind('@');
}
// Find the dot separating the primary image name from the extension.
size_t last_dot = image_location.rfind('.'); // Extract the extension and base (the path and primary image name).
std::string extension;
std::string base = image_location; if (last_dot != std::string::npos && last_dot > last_slash) {
extension = image_location.substr(last_dot); // Including the dot.
base.resize(last_dot);
} // For non-empty primary image name, add '-' to the `base`. if (last_slash + 1u != base.size()) {
base += '-';
}
// Now create the other names. Use a counted loop to skip the first one if needed. for (size_t i = start_index; i < dex_locations.size(); ++i) { // Replace path with `base` (i.e. image path and prefix) and replace the original // extension (if any) with `extension`.
std::string name = dex_locations[i];
size_t last_dex_slash = name.rfind('/'); if (last_dex_slash != std::string::npos) {
name = name.substr(last_dex_slash + 1);
}
size_t last_dex_dot = name.rfind('.'); if (last_dex_dot != std::string::npos) {
name.resize(last_dex_dot);
}
locations.push_back(ART_FORMAT("{}{}{}", base, name, extension));
} return locations;
}
void ImageSpace::DumpSections(std::ostream& os) const { const uint8_t* base = Begin(); const ImageHeader& header = GetImageHeader(); for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { auto section_type = static_cast<ImageHeader::ImageSections>(i); const ImageSection& section = header.GetImageSection(section_type);
os << section_type << " " << reinterpret_cast<constvoid*>(base + section.Offset())
<< "-" << reinterpret_cast<constvoid*>(base + section.End()) << "\n";
}
}
void ImageSpace::ReleaseMetadata() { const ImageSection& metadata = GetImageHeader().GetMetadataSection();
VLOG(image) << "Releasing " << metadata.Size() << " image metadata bytes"; // Avoid using ZeroAndReleasePages since the zero fill might not be word atomic.
uint8_t* const page_begin = AlignUp(Begin() + metadata.Offset(), gPageSize);
uint8_t* const page_end = AlignDown(Begin() + metadata.End(), gPageSize); if (page_begin < page_end) {
CHECK_NE(madvise(page_begin, page_end - page_begin, MADV_DONTNEED), -1) << "madvise failed";
}
}
} // namespace space
} // namespace gc
} // namespace art
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.73Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.