in runtime/class_linker.cc [1562:1820]
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
jobjectArray dex_elements,
const char* dex_location,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
std::string* error_msg) {
DCHECK(out_dex_files != nullptr);
DCHECK(error_msg != nullptr);
const uint64_t start_time = NanoTime();
const bool app_image = class_loader.Get() != nullptr;
const ImageHeader& header = space->GetImageHeader();
mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
DCHECK(dex_caches_object != nullptr);
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
Thread* const self = Thread::Current();
StackHandleScope<2> hs(self);
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
const OatFile* oat_file = space->GetOatFile();
std::unordered_set<mirror::ClassLoader*> image_class_loaders;
// Check that the image is what we are expecting.
if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) {
*error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu",
static_cast<size_t>(space->GetImageHeader().GetPointerSize()),
image_pointer_size_);
return false;
}
DCHECK(class_roots.Get() != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
class_roots->GetLength(),
static_cast<int32_t>(kClassRootsMax));
return false;
}
// Check against existing class roots to make sure they match the ones in the boot image.
for (size_t i = 0; i < kClassRootsMax; i++) {
if (class_roots->Get(i) != GetClassRoot(static_cast<ClassRoot>(i))) {
*error_msg = "App image class roots must have pointer equality with runtime ones.";
return false;
}
}
if (oat_file->GetOatHeader().GetDexFileCount() !=
static_cast<uint32_t>(dex_caches->GetLength())) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
"image";
return false;
}
StackHandleScope<1> hs2(self);
MutableHandle<mirror::DexCache> h_dex_cache(hs2.NewHandle<mirror::DexCache>(nullptr));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
h_dex_cache.Assign(dex_caches->Get(i));
std::string dex_file_location(h_dex_cache->GetLocation()->ToModifiedUtf8());
// TODO: Only store qualified paths.
// If non qualified, qualify it.
if (dex_file_location.find('/') == std::string::npos) {
std::string dex_location_path = dex_location;
const size_t pos = dex_location_path.find_last_of('/');
CHECK_NE(pos, std::string::npos);
dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/'
dex_file_location = dex_location_path + dex_file_location;
}
std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
dex_file_location.c_str(),
error_msg);
if (dex_file == nullptr) {
return false;
}
if (app_image) {
// The current dex file field is bogus, overwrite it so that we can get the dex file in the
// loop below.
h_dex_cache->SetDexFile(dex_file.get());
// Check that each class loader resolved the same way.
// TODO: Store image class loaders as image roots.
GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
mirror::Class* klass = types[j].Read();
if (klass != nullptr) {
DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
mirror::ClassLoader* image_class_loader = klass->GetClassLoader();
image_class_loaders.insert(image_class_loader);
}
}
} else {
if (kSanityCheckObjects) {
SanityCheckArtMethodPointerArray(h_dex_cache->GetResolvedMethods(),
h_dex_cache->NumResolvedMethods(),
image_pointer_size_,
heap->GetBootImageSpaces());
}
// Register dex files, keep track of existing ones that are conflicts.
AppendToBootClassPath(*dex_file.get(), h_dex_cache);
}
out_dex_files->push_back(std::move(dex_file));
}
if (app_image) {
ScopedObjectAccessUnchecked soa(Thread::Current());
// Check that the class loader resolves the same way as the ones in the image.
// Image class loader [A][B][C][image dex files]
// Class loader = [???][dex_elements][image dex files]
// Need to ensure that [???][dex_elements] == [A][B][C].
// For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic
// for PathClassLoader does this by looping through the array of dex files. To ensure they
// resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
// and check that the dex file names are the same.
for (mirror::ClassLoader* image_class_loader : image_class_loaders) {
if (IsBootClassLoader(soa, image_class_loader)) {
// The dex cache can reference types from the boot class loader.
continue;
}
std::list<mirror::String*> image_dex_file_names;
std::string temp_error_msg;
if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) {
*error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
temp_error_msg.c_str());
return false;
}
std::list<mirror::String*> loader_dex_file_names;
if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
*error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
temp_error_msg.c_str());
return false;
}
// Add the temporary dex path list elements at the end.
auto* elements = soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements);
for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
mirror::Object* element = elements->GetWithoutChecks(i);
if (element != nullptr) {
// If we are somewhere in the middle of the array, there may be nulls at the end.
loader_dex_file_names.push_back(GetDexPathListElementName(soa, element));
}
}
// Ignore the number of image dex files since we are adding those to the class loader anyways.
CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
static_cast<size_t>(dex_caches->GetLength()));
size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
// Check that the dex file names match.
bool equal = image_count == loader_dex_file_names.size();
if (equal) {
auto it1 = image_dex_file_names.begin();
auto it2 = loader_dex_file_names.begin();
for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
equal = equal && (*it1)->Equals(*it2);
}
}
if (!equal) {
VLOG(image) << "Image dex files " << image_dex_file_names.size();
for (mirror::String* name : image_dex_file_names) {
VLOG(image) << name->ToModifiedUtf8();
}
VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
for (mirror::String* name : loader_dex_file_names) {
VLOG(image) << name->ToModifiedUtf8();
}
*error_msg = "Rejecting application image due to class loader mismatch";
// Ignore class loader mismatch for now since these would just use possibly incorrect
// oat code anyways. The structural class check should be done in the parent.
}
}
}
if (kSanityCheckObjects) {
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
auto* dex_cache = dex_caches->Get(i);
for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
if (field != nullptr) {
CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
}
}
}
if (!app_image) {
heap->VisitObjects(SanityCheckObjectsCallback, nullptr);
}
}
// Set entry point to interpreter if in InterpretOnly mode.
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
}
ClassTable* class_table = nullptr;
{
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table = InsertClassTableForClassLoader(class_loader.Get());
}
// If we have a class table section, read it and use it for verification in
// UpdateAppImageClassLoadersAndDexCaches.
ClassTable::ClassSet temp_set;
const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable);
const bool added_class_table = class_table_section.Size() > 0u;
if (added_class_table) {
const uint64_t start_time2 = NanoTime();
size_t read_count = 0;
temp_set = ClassTable::ClassSet(space->Begin() + class_table_section.Offset(),
/*make copy*/false,
&read_count);
if (!app_image) {
dex_cache_boot_image_class_lookup_required_ = false;
}
VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
}
if (app_image) {
bool forward_dex_cache_arrays = false;
if (!UpdateAppImageClassLoadersAndDexCaches(space,
class_loader,
dex_caches,
added_class_table ? &temp_set : nullptr,
/*out*/&forward_dex_cache_arrays,
/*out*/error_msg)) {
return false;
}
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
class_loader.Get(),
forward_dex_cache_arrays);
if (added_class_table) {
for (GcRoot<mirror::Class>& root : temp_set) {
visitor(root.Read());
}
}
// forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
// In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
// mark as PROT_NONE to catch any invalid accesses.
if (forward_dex_cache_arrays) {
const ImageSection& dex_cache_section = header.GetImageSection(
ImageHeader::kSectionDexCacheArrays);
uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
if (section_begin < section_end) {
madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
mprotect(section_begin, section_end - section_begin, PROT_NONE);
VLOG(image) << "Released and protected dex cache array image section from "
<< reinterpret_cast<const void*>(section_begin) << "-"
<< reinterpret_cast<const void*>(section_end);
}
}
}
if (added_class_table) {
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table->AddClassSet(std::move(temp_set));
}
if (kIsDebugBuild && app_image) {
// This verification needs to happen after the classes have been added to the class loader.
// Since it ensures classes are in the class table.
VerifyClassInTableArtMethodVisitor visitor2(class_table);
header.VisitPackedArtMethods(&visitor2, space->Begin(), sizeof(void*));
}
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
}