#ifndef __APPLE__ #include <link.h> // for dl_iterate_phdr. #endif
#ifdef __GLIBC__ #include <gnu/libc-version.h> // for gnu_get_libc_version. // strverscmp is part of the GNU/Linux extension, so define _GNU_SOURCE before including // string.h, and undefine it afterward if it is not already defined. #ifndef _GNU_SOURCE #define _GNU_SOURCE #define DEFINED_GNU_SOURCE #endif #include <string.h> // for strverscmp #ifdef DEFINED_GNU_SOURCE #undef _GNU_SOURCE #undef DEFINED_GNU_SOURCE #endif #endif
// dlopen_ext support from bionic. #ifdef ART_TARGET_ANDROID #include"android/dlext.h" #include"bionic/dlext_namespaces.h" #endif
namespace art HIDDEN {
using android::base::StringAppendV; using android::base::StringPrintf;
// Whether OatFile::Open will try dlopen. Fallback is our own ELF loader. static constexpr bool kUseDlopen = true;
// Whether OatFile::Open will try dlopen on the host. On the host we're not linking against // bionic, so cannot take advantage of the support for changed semantics (loading the same soname // multiple times). However, if/when we switch the above, we likely want to switch this, too, // to get test coverage of the code paths. static constexpr bool kUseDlopenOnHost = true;
// For debugging, Open will print DlOpen error message if set to true. static constexpr bool kPrintDlOpenErrorMessage = false;
// Returns whether dlopen can load dynamic shared objects with a read-only .dynamic section. // According to the ELF spec whether .dynamic is writable or not is determined by the operating // system and processor (Book I, part 1 "Object Files", "Special sections"). Bionic and glibc // >= 2.35 support read-only .dynamic. Older glibc versions have a bug that causes a crash if // this section is read-only: https://sourceware.org/bugzilla/show_bug.cgi?id=28340. bool IsReadOnlyDynamicSupportedByDlOpen() { // The following lambda will be executed only once as a part of a static // variable initialization. #ifdef __GLIBC__ staticbool is_ro_dynamic_supported = []() { // libc version has the following format: // "X.Y" // where: // X - major version in the decimal format. // Y - minor version in the decimal format. // for example: // "2.34" constchar* libc_version = gnu_get_libc_version(); return strverscmp(libc_version, "2.35") >= 0;
}(); return is_ro_dynamic_supported; #else returntrue; #endif
}
// Note for OatFileBase and descendents: // // These are used in OatFile::Open to try all our loaders. // // The process is simple: // // 1) Allocate an instance through the standard constructor (location, executable) // 2) Load() to try to open the file. // 3) ComputeFields() to populate the OatFile fields like begin_, using FindDynamicSymbolAddress. // 4) PreSetup() for any steps that should be done before the final setup. // 5) Setup() to complete the procedure.
class OatFileBase : public OatFile { public: virtual ~OatFileBase() {}
template <typename kOatFileBaseSubType> static OatFileBase* OpenOatFile(int zip_fd, const std::string& vdex_filename, const std::string& elf_filename, const std::string& location, bool executable, bool low_4gb,
ArrayRef<const std::string> dex_filenames,
ArrayRef<File> dex_files, /*inout*/ MemMap* reservation, // Where to load if not null. /*out*/ std::string* error_msg);
template <typename kOatFileBaseSubType> static OatFileBase* OpenOatFile(int zip_fd, int vdex_fd, int oat_fd, const std::string& vdex_filename, const std::string& oat_filename, bool executable, bool low_4gb,
ArrayRef<const std::string> dex_filenames,
ArrayRef<File> dex_files, /*inout*/ MemMap* reservation, // Where to load if not null. /*out*/ std::string* error_msg);
struct stat sdm_st; if (stat(sdm_filename.c_str(), &sdm_st) != 0) {
*error_msg = ART_FORMAT("Failed to stat sdm file '{}': {}", sdm_filename, strerror(errno)); return nullptr;
}
std::unique_ptr<SdcReader> sdc_reader = SdcReader::Load(sdc_filename, error_msg); if (sdc_reader == nullptr) { return nullptr;
} if (sdc_reader->GetSdmTimestampNs() != TimeSpecToNs(sdm_st.st_mtim)) { // The sdm file had been replaced after the sdc file was created.
*error_msg = ART_FORMAT("Obsolete sdc file '{}'", sdc_filename); return nullptr;
} // The apex-versions value in the sdc file, written by ART Service, is the value of // `Runtime::GetApexVersions` at the time where the sdm file was first seen on device. We use it // to override the APEX versions in the oat header. This is for detecting samegrade placebos.
ret->override_apex_versions_ = sdc_reader->GetApexVersions();
if (!ret->Load(elf_filename, executable, /*low_4gb=*/false, /*reservation=*/nullptr, error_msg)) { return nullptr;
}
if (!ret->ComputeFields(elf_filename, error_msg)) { return nullptr;
}
bool OatFileBase::ComputeFields(const std::string& file_path, std::string* error_msg) {
std::string symbol_error_msg;
begin_ = FindDynamicSymbolAddress("oatdata", &symbol_error_msg); if (begin_ == nullptr) {
*error_msg = StringPrintf("Failed to find oatdata symbol in '%s' %s",
file_path.c_str(),
symbol_error_msg.c_str()); returnfalse;
}
end_ = FindDynamicSymbolAddress("oatlastword", &symbol_error_msg); if (end_ == nullptr) {
*error_msg = StringPrintf("Failed to find oatlastword symbol in '%s' %s",
file_path.c_str(),
symbol_error_msg.c_str()); returnfalse;
} // Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
data_img_rel_ro_begin_ = FindDynamicSymbolAddress("oatdataimgrelro", &symbol_error_msg); if (data_img_rel_ro_begin_ != nullptr) {
data_img_rel_ro_end_ =
FindDynamicSymbolAddress("oatdataimgrelrolastword", &symbol_error_msg); if (data_img_rel_ro_end_ == nullptr) {
*error_msg =
StringPrintf("Failed to find oatdataimgrelrolastword symbol in '%s'", file_path.c_str()); returnfalse;
} // Readjust to be non-inclusive upper bound.
data_img_rel_ro_end_ += sizeof(uint32_t);
data_img_rel_ro_app_image_ =
FindDynamicSymbolAddress("oatdataimgrelroappimage", &symbol_error_msg); if (data_img_rel_ro_app_image_ == nullptr) {
data_img_rel_ro_app_image_ = data_img_rel_ro_end_;
}
}
bss_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbss", &symbol_error_msg)); if (bss_begin_ == nullptr) { // No .bss section.
bss_end_ = nullptr;
} else {
bss_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbsslastword", &symbol_error_msg)); if (bss_end_ == nullptr) {
*error_msg = StringPrintf("Failed to find oatbsslastword symbol in '%s'", file_path.c_str()); returnfalse;
} // Readjust to be non-inclusive upper bound.
bss_end_ += sizeof(uint32_t); // Find bss methods if present.
bss_methods_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssmethods", &symbol_error_msg)); // Find bss roots if present.
bss_roots_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssroots", &symbol_error_msg)); // Find bss strings if present.
bss_strings_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssstrings", &symbol_error_msg));
}
returntrue;
}
// Read an unaligned entry from the OatDexFile data in OatFile and advance the read // position by the number of bytes read, i.e. sizeof(T). // Return true on success, false if the read would go beyond the end of the OatFile. template <typename T> inlinestaticbool ReadOatDexFileData(const OatFile& oat_file, /*inout*/const uint8_t** oat, /*out*/T* value) {
DCHECK(oat != nullptr);
DCHECK(value != nullptr);
DCHECK_LE(*oat, oat_file.End()); if (UNLIKELY(static_cast<size_t>(oat_file.End() - *oat) < sizeof(T))) { returnfalse;
}
static_assert(std::is_trivial<T>::value, "T must be a trivial type"); using unaligned_type __attribute__((__aligned__(1))) = T;
*value = *reinterpret_cast<const unaligned_type*>(*oat);
*oat += sizeof(T); returntrue;
}
if (found_size == 0) {
*type_lookup_table_data = nullptr; returntrue;
}
*type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t); if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data, found_size))) {
*error_msg =
StringPrintf("In vdex file '%s' found invalid type lookup table data %p of size %zu " "not in [%p, %p]",
vdex_file->GetName().c_str(),
type_lookup_table_data,
found_size,
vdex_file->Begin(),
vdex_file->End()); returnfalse;
} if (UNLIKELY(!IsAligned<4>(type_lookup_table_start))) {
*error_msg =
StringPrintf("In vdex file '%s' found invalid type lookup table alignment %p",
vdex_file->GetName().c_str(),
type_lookup_table_start); returnfalse;
} returntrue;
}
bool OatFileBase::Setup(const std::vector<const DexFile*>& dex_files, std::string* error_msg) { const uint8_t* type_lookup_table_start = nullptr; for (size_t i = 0; i < dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; // Defensively verify external dex file checksum. `OatFileAssistant` // expects this check to happen during oat file setup when the oat file // does not contain dex code. if (dex_file->GetLocationChecksum() != vdex_->GetLocationChecksum(i)) {
*error_msg = StringPrintf("Dex checksum does not match for %s, dex has %d, vdex has %d",
dex_file->GetLocation().c_str(),
dex_file->GetLocationChecksum(),
vdex_->GetLocationChecksum(i)); returnfalse;
}
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
type_lookup_table_start = vdex_->GetNextTypeLookupTableData(type_lookup_table_start, i); const uint8_t* type_lookup_table_data = nullptr; if (!ComputeAndCheckTypeLookupTableData(dex_file->GetHeader(),
type_lookup_table_start,
vdex_.get(),
&type_lookup_table_data,
error_msg)) { returnfalse;
} // Create an OatDexFile and add it to the owning container.
OatDexFile* oat_dex_file = new OatDexFile(this,
dex_file->GetContainer(),
dex_file->Begin(),
dex_file->GetHeader().magic_,
dex_file->GetLocationChecksum(),
dex_file->GetSha1(),
dex_location,
canonical_location,
type_lookup_table_data,
i);
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
std::string_view key(oat_dex_file->GetDexFileLocation());
oat_dex_files_.Put(key, oat_dex_file); if (canonical_location != dex_location) {
std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
} // Now that we've created all the OatDexFile, update the dex files. for (size_t i = 0; i < dex_files.size(); ++i) {
dex_files[i]->SetOatDexFile(oat_dex_files_storage_[i]);
} returntrue;
}
DCHECK_GE(static_cast<size_t>(pointer_size), alignof(GcRoot<mirror::Object>)); // In certain cases, ELF can be mapped at an address which is page aligned, // however not aligned to kElfSegmentAlignment. While technically this isn't // correct as per requirement in the ELF header, it has to be supported for // now. See also the comment at ImageHeader::RelocateImageReferences. if (!IsAlignedParam(bss_begin_, MemMap::GetPageSize()) ||
!IsAlignedParam(bss_methods_, static_cast<size_t>(pointer_size)) ||
!IsAlignedParam(bss_roots_, static_cast<size_t>(pointer_size)) ||
!IsAlignedParam(bss_strings_, sizeof(GcRoot<mirror::Object>)) ||
!IsAligned<alignof(GcRoot<mirror::Object>)>(bss_end_)) {
*error_msg = ErrorPrintf( "unaligned bss symbol(s): begin = %p, methods_ = %p, roots = %p, strings = %p, end = %p",
bss_begin_,
bss_methods_,
bss_roots_,
bss_strings_,
bss_end_); returnfalse;
}
// Location encoded in the oat file. We will use this for multidex naming.
std::string_view oat_dex_file_location(dex_file_location_data, dex_file_location_size);
std::string dex_file_location(oat_dex_file_location); bool is_multidex = DexFileLoader::IsMultiDexLocation(dex_file_location); // Check that `is_multidex` does not clash with other indicators. The first dex location // must be primary location and, if we're opening external dex files, the location must // be multi-dex if and only if we already have a dex file opened for it. if ((i == 0 && is_multidex) ||
(!external_dex_files_.empty() && (is_multidex != (i < external_dex_files_.size())))) {
*error_msg = ErrorPrintf("unexpected %s location '%s'",
is_multidex ? "multi-dex" : "primary",
dex_file_location.c_str()); returnfalse;
} // Remember the primary location and, if provided, the replacement from `dex_filenames`. if (!is_multidex) {
primary_location = oat_dex_file_location; if (!dex_filenames.empty()) { if (dex_filenames_pos == dex_filenames.size()) {
*error_msg = ErrorPrintf( "excessive primary location '%s', expected only %zu primary locations",
dex_file_location.c_str(),
dex_filenames.size()); returnfalse;
}
primary_location_replacement = dex_filenames[dex_filenames_pos];
dex_file = dex_filenames_pos < dex_files.size() ? &dex_files[dex_filenames_pos] : &no_file;
++dex_filenames_pos;
}
} // Check that the base location of a multidex location matches the last seen primary location. if (is_multidex && DexFileLoader::GetBaseLocation(dex_file_location) != primary_location) {
*error_msg = ErrorPrintf("unexpected multidex location '%s', unrelated to '%s'",
dex_file_location.c_str(),
std::string(primary_location).c_str()); returnfalse;
}
std::string dex_file_name = dex_file_location; if (!dex_filenames.empty()) {
dex_file_name.replace(/*pos*/ 0u, primary_location.size(), primary_location_replacement); // If the location (the `--dex-location` passed to dex2oat) only contains the basename and // matches the basename in the provided file name, use the provided file name also as the // location. // This is needed when the location on device is unknown at compile-time, typically during // Cloud Compilation because the compilation is done on the server and the apk is later // installed on device into `/data/app/<random_string>`. // This is not needed during dexpreopt because the location on device is known to be a certain // location in /system, /product, etc. if (dex_file_location.find('/') == std::string::npos &&
dex_file_name.size() > dex_file_location.size() &&
dex_file_name[dex_file_name.size() - dex_file_location.size() - 1u] == '/' &&
dex_file_name.ends_with(dex_file_location)) {
dex_file_location = dex_file_name;
}
}
DexFile::Magic dex_file_magic; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_magic))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' truncated after dex file magic",
i,
dex_file_location.c_str()); returnfalse;
}
uint32_t dex_file_checksum; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_checksum))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' truncated after dex file checksum",
i,
dex_file_location.c_str()); returnfalse;
}
DexFile::Sha1 dex_file_sha1; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_sha1))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' truncated after dex file sha1",
i,
dex_file_location.c_str()); returnfalse;
}
uint32_t dex_file_offset; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_file_offset))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' truncated after dex file offsets",
i,
dex_file_location.c_str()); returnfalse;
} if (UNLIKELY(dex_file_offset > DexSize())) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with dex file offset %u > %zu",
i,
dex_file_location.c_str(),
dex_file_offset,
DexSize()); returnfalse;
}
std::shared_ptr<DexFileContainer> dex_file_container; const uint8_t* dex_file_pointer = nullptr; if (UNLIKELY(dex_file_offset == 0U)) { // Do not support mixed-mode oat files. if (i != 0u && external_dex_files_.empty()) {
*error_msg = ErrorPrintf("unsupported uncompressed-dex-file for dex file %zu (%s)",
i,
dex_file_location.c_str()); returnfalse;
}
DCHECK_LE(i, external_dex_files_.size()); if (i == external_dex_files_.size()) {
std::vector<std::unique_ptr<const DexFile>> new_dex_files; // No dex files, load it from location. bool loaded = false;
CHECK(zip_fd == -1 || dex_files.empty()); // Allow only the supported combinations. if (zip_fd != -1) {
File file(zip_fd, /*check_usage=*/false);
ArtDexFileLoader dex_file_loader(&file, dex_file_location);
loaded = dex_file_loader.Open( /*verify=*/false, /*verify_checksum=*/false, error_msg, &new_dex_files);
} elseif (dex_file->IsValid()) { // Note that we assume dex_fds are backing by jars.
ArtDexFileLoader dex_file_loader(dex_file, dex_file_location);
loaded = dex_file_loader.Open( /*verify=*/false, /*verify_checksum=*/false, error_msg, &new_dex_files);
} else {
ArtDexFileLoader dex_file_loader(dex_file_name.c_str(), dex_file_location);
loaded = dex_file_loader.Open( /*verify=*/false, /*verify_checksum=*/false, error_msg, &new_dex_files);
} if (!loaded) { if (Runtime::Current() == nullptr) { // If there's no runtime, we're running oatdump, so return // a half constructed oat file that oatdump knows how to deal with.
LOG(WARNING) << "Could not find associated dex files of oat file. "
<< "Oatdump will only dump the header."; returntrue;
} returnfalse;
} // The oat file may be out of date wrt/ the dex-file location. We need to be defensive // here and ensure that at least the number of dex files still matches. // If we have a zip_fd, or reached the end of provided `dex_filenames`, we must // load all dex files from that file, otherwise we may open multiple files. // Note: actual checksum comparisons are the duty of the OatFileAssistant and will be // done after loading the OatFile.
size_t max_dex_files = dex_file_count - external_dex_files_.size(); bool expect_all =
(zip_fd != -1) || (!dex_filenames.empty() && dex_filenames_pos == dex_filenames.size()); if (expect_all ? new_dex_files.size() != max_dex_files
: new_dex_files.size() > max_dex_files) {
*error_msg = ErrorPrintf("expected %s%zu uncompressed dex files, but found %zu in '%s'",
(expect_all ? "" : "<="),
max_dex_files,
new_dex_files.size(),
dex_file_location.c_str()); returnfalse;
} for (std::unique_ptr<const DexFile>& dex_file_ptr : new_dex_files) {
external_dex_files_.push_back(std::move(dex_file_ptr));
}
} // Defensively verify external dex file checksum. `OatFileAssistant` // expects this check to happen during oat file setup when the oat file // does not contain dex code. if (dex_file_checksum != external_dex_files_[i]->GetLocationChecksum()) { // The location checksum is affected by all dex files within container, // so one dex file can match exactly by SHA1 and yet location checksum // might differ for it if some other dex file in the container differs.
*error_msg = ErrorPrintf("dex file checksum 0x%08x does not match" " checksum 0x%08x of external dex file '%s'",
dex_file_checksum,
external_dex_files_[i]->GetLocationChecksum(),
external_dex_files_[i]->GetLocation().c_str()); returnfalse;
}
CHECK(dex_file_sha1 == external_dex_files_[i]->GetSha1());
dex_file_container = external_dex_files_[i]->GetContainer();
dex_file_pointer = external_dex_files_[i]->Begin();
} else { // Do not support mixed-mode oat files. if (!external_dex_files_.empty()) {
*error_msg = ErrorPrintf("unsupported embedded dex-file for dex file %zu (%s)",
i,
dex_file_location.c_str()); returnfalse;
} if (UNLIKELY(DexSize() - dex_file_offset < sizeof(DexFile::Header))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with dex file " "offset %u of %zu but the size of dex file header is %zu",
i,
dex_file_location.c_str(),
dex_file_offset,
DexSize(), sizeof(DexFile::Header)); returnfalse;
}
dex_file_container = std::make_shared<MemoryDexFileContainer>(DexBegin(), DexEnd());
dex_file_pointer = DexBegin() + dex_file_offset;
}
constbool valid_magic = DexFileLoader::IsMagicValid(dex_file_pointer); if (UNLIKELY(!valid_magic)) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with invalid dex file magic",
i,
dex_file_location.c_str()); returnfalse;
} if (UNLIKELY(!DexFileLoader::IsVersionAndMagicValid(dex_file_pointer))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with invalid dex file version",
i,
dex_file_location.c_str()); returnfalse;
} const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer); if (dex_file_offset != 0 && (DexSize() - dex_file_offset < header->file_size_)) {
*error_msg = ErrorPrintf( "OatDexFile #%zu for '%s' with dex file offset %u and size %u truncated at %zu",
i,
dex_file_location.c_str(),
dex_file_offset,
header->file_size_,
DexSize()); returnfalse;
}
uint32_t class_offsets_offset; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &class_offsets_offset))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' truncated after class offsets offset",
i,
dex_file_location.c_str()); returnfalse;
} if (UNLIKELY(class_offsets_offset > Size()) ||
UNLIKELY((Size() - class_offsets_offset) / sizeof(uint32_t) < header->class_defs_size_)) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with truncated " "class offsets, offset %u of %zu, class defs %u",
i,
dex_file_location.c_str(),
class_offsets_offset,
Size(),
header->class_defs_size_); returnfalse;
} if (UNLIKELY(!IsAligned<alignof(uint32_t)>(class_offsets_offset))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with unaligned " "class offsets, offset %u",
i,
dex_file_location.c_str(),
class_offsets_offset); returnfalse;
} const uint32_t* class_offsets_pointer = reinterpret_cast<const uint32_t*>(Begin() + class_offsets_offset);
uint32_t lookup_table_offset; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &lookup_table_offset))) {
*error_msg = ErrorPrintf("OatDexFile #%zd for '%s' truncated after lookup table offset",
i,
dex_file_location.c_str()); returnfalse;
} const uint8_t* lookup_table_data = lookup_table_offset != 0u
? DexBegin() + lookup_table_offset
: nullptr; if (lookup_table_offset != 0u &&
(UNLIKELY(lookup_table_offset > DexSize()) ||
UNLIKELY(DexSize() - lookup_table_offset <
TypeLookupTable::RawDataLength(header->class_defs_size_)))) {
*error_msg = ErrorPrintf("OatDexFile #%zu for '%s' with truncated type lookup table, " "offset %u of %zu, class defs %u",
i,
dex_file_location.c_str(),
lookup_table_offset,
Size(),
header->class_defs_size_); returnfalse;
}
uint32_t dex_profile_metadata_offset; if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &dex_profile_metadata_offset))) {
*error_msg =
ErrorPrintf("OatDexFile #%zd for '%s' truncated after dex profile metadata offset",
i,
dex_file_location.c_str()); returnfalse;
} const DexProfileMetadata* dex_profile_metadata =
dex_profile_metadata_offset != 0
? reinterpret_cast<const DexProfileMetadata*>(Begin() + dex_profile_metadata_offset)
: nullptr;
BssMappingInfo bss_mapping_info; if (!ReadBssMappingInfo(
&oat, "OatDexFile", i, dex_file_location, &bss_mapping_info, error_msg)) { returnfalse;
}
// Create the OatDexFile and add it to the owning container.
OatDexFile* oat_dex_file = new OatDexFile(this,
dex_file_location,
DexFileLoader::GetDexCanonicalLocation(dex_file_name.c_str()),
dex_file_magic,
dex_file_checksum,
dex_file_sha1,
dex_file_container,
dex_file_pointer,
lookup_table_data,
bss_mapping_info,
class_offsets_pointer,
dex_profile_metadata,
i);
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table. // Note: We do not add the non-canonical `dex_file_name`. If it is different from both // the location and canonical location, GetOatDexFile() shall canonicalize it when // requested and match the canonical path.
std::string_view key = oat_dex_file_location; // References oat file data.
std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
oat_dex_files_.Put(key, oat_dex_file); if (canonical_key != key) {
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
size_t bcp_info_offset = GetOatHeader().GetBcpBssInfoOffset(); // `bcp_info_offset` will be 0 for multi-image, or for the case of no mappings. if (bcp_info_offset != 0) { // Consistency check. if (bcp_info_offset < GetOatHeader().GetHeaderSize() || bcp_info_offset > Size()) {
*error_msg = ErrorPrintf("invalid bcp info offset: %zu is not in [%zu, %zu]",
bcp_info_offset,
GetOatHeader().GetHeaderSize(),
Size()); returnfalse;
} const uint8_t* bcp_info_begin = Begin() + bcp_info_offset; // Jump to the BCP_info records.
uint32_t number_of_bcp_dexfiles; if (UNLIKELY(!ReadOatDexFileData(*this, &bcp_info_begin, &number_of_bcp_dexfiles))) {
*error_msg = ErrorPrintf("failed to read the number of BCP dex files"); returnfalse;
}
Runtime* const runtime = Runtime::Current();
ClassLinker* const linker = runtime != nullptr ? runtime->GetClassLinker() : nullptr; if (linker != nullptr && UNLIKELY(number_of_bcp_dexfiles > linker->GetBootClassPath().size())) { // If we compiled with more DexFiles than what we have at runtime, we expect to discard this // OatFile after verifying its checksum in OatFileAssistant. Therefore, we set // `number_of_bcp_dexfiles` to 0 to avoid reading data that will ultimately be discarded.
number_of_bcp_dexfiles = 0;
}
DCHECK(bcp_bss_info_.empty());
bcp_bss_info_.resize(number_of_bcp_dexfiles); // At runtime, there might be more DexFiles added to the BCP that we didn't compile with. // We only care about the ones in [0..number_of_bcp_dexfiles). for (size_t i = 0, size = number_of_bcp_dexfiles; i != size; ++i) { const std::string& dex_file_location = linker != nullptr
? linker->GetBootClassPath()[i]->GetLocation()
: "No runtime/linker therefore no DexFile location"; if (!ReadBssMappingInfo(
&bcp_info_begin, "BcpBssInfo", i, dex_file_location, &bcp_bss_info_[i], error_msg)) { returnfalse;
}
}
}
if (DataImgRelRoBegin() != nullptr) { // Make .data.img.rel.ro read only. ClassLinker shall temporarily make it writable for // relocation when we register a dex file from this oat file. We do not do the relocation // here to avoid dirtying the pages if the code is never actually ready to be executed.
uint8_t* reloc_begin = const_cast<uint8_t*>(DataImgRelRoBegin());
CheckedCall(mprotect, "protect relocations", reloc_begin, DataImgRelRoSize(), PROT_READ); // Make sure the file lists a boot image dependency, otherwise the .data.img.rel.ro // section is bogus. The full dependency is checked before the code is executed. // We cannot do this check if we do not have a key-value store, i.e. for secondary // oat files for boot image extensions. if (GetOatHeader().GetKeyValueStoreSize() != 0u) { constchar* boot_class_path_checksum =
GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey); if (boot_class_path_checksum == nullptr ||
boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
*error_msg = ErrorPrintf(".data.img.rel.ro section present without boot image dependency."); returnfalse;
}
}
}
returntrue;
}
//////////////////////// // OatFile via dlopen // ////////////////////////
class DlOpenOatFile final : public OatFileBase { public:
DlOpenOatFile(const std::string& filename, bool executable)
: OatFileBase(filename, executable),
dlopen_handle_(nullptr),
shared_objects_before_(0) {
}
private: bool Dlopen(const std::string& elf_filename, /*inout*/MemMap* reservation, // Where to load if not null. /*out*/std::string* error_msg);
// On the host, if the same library is loaded again with dlopen the same // file handle is returned. This differs from the behavior of dlopen on the // target, where dlopen reloads the library at a different address every // time you load it. The runtime relies on the target behavior to ensure // each instance of the loaded library has a unique dex cache. To avoid // problems, we fall back to our own linker in the case when the same // library is opened multiple times on host. dlopen_handles_ is used to // detect that case. // Guarded by host_dlopen_handles_lock_; static std::unordered_set<void*> host_dlopen_handles_;
// Reservation and placeholder memory map objects corresponding to the regions mapped by dlopen. // Note: Must be destroyed after dlclose() as it can hold the owning reservation.
std::vector<MemMap> dlopen_mmaps_;
// dlopen handle during runtime. void* dlopen_handle_; // TODO: Unique_ptr with custom deleter.
// The number of shared objects the linker told us about before loading. Used to // (optimistically) optimize the PreSetup stage (see comment there).
size_t shared_objects_before_;
bool DlOpenOatFile::Load(const std::string& elf_filename, bool executable, bool low_4gb, /*inout*/ MemMap* reservation, // Where to load if not null. /*out*/ std::string* error_msg) { // Use dlopen only when flagged to do so, and when it's OK to load things executable. // TODO: Also try when not executable? The issue here could be re-mapping as writable (as // !executable is a sign that we may want to patch), which may not be allowed for // various reasons. if (!kUseDlopen) {
*error_msg = "DlOpen is disabled."; returnfalse;
} if (low_4gb) {
*error_msg = "DlOpen does not support low 4gb loading."; returnfalse;
} if (!executable) {
*error_msg = "DlOpen does not support non-executable loading."; returnfalse;
}
if (!IsReadOnlyDynamicSupportedByDlOpen()) {
*error_msg = "DlOpen does not support read-only .dynamic section."; returnfalse;
}
// dlopen always returns the same library if it is already opened on the host. For this reason // we only use dlopen if we are the target or we do not already have the dex file opened. Having // the same library loaded multiple times at different addresses is required for class unloading // and for having dex caches arrays in the .bss section. if (!kIsTargetBuild) { if (!kUseDlopenOnHost) {
*error_msg = "DlOpen disabled for host."; returnfalse;
}
}
#ifdef ART_TARGET_ANDROID staticstruct android_namespace_t* GetSystemLinkerNamespace() { staticstruct android_namespace_t* system_ns = []() { // The system namespace is called "default" for binaries in /system and // "system" for those in the ART APEX. Try "system" first since "default" // always exists. // TODO(b/185587109): Get rid of this error prone logic. struct android_namespace_t* ns = android_get_exported_namespace("system"); if (ns == nullptr) {
ns = android_get_exported_namespace("default"); if (ns == nullptr) {
LOG(FATAL) << "Failed to get system namespace for loading OAT files";
}
} return ns;
}(); return system_ns;
} #endif// ART_TARGET_ANDROID
bool DlOpenOatFile::Dlopen(const std::string& elf_filename, /*inout*/MemMap* reservation, /*out*/std::string* error_msg) { #ifdef __APPLE__ // The dl_iterate_phdr syscall is missing. There is similar API on OSX, // but let's fallback to the custom loading code for the time being.
UNUSED(elf_filename, reservation);
*error_msg = "Dlopen unsupported on Mac."; returnfalse; #else
{ // `elf_filename` is in the format of `/path/to/oat` or `/path/to/zip!/primary.odex`. We can // reuse `GetDexCanonicalLocation` to resolve the real path of the part before "!" even though // `elf_filename` does not refer to a dex file.
static_assert(std::string_view(kZipSeparator).starts_with(DexFileLoader::kMultiDexSeparator));
std::string absolute_path = DexFileLoader::GetDexCanonicalLocation(elf_filename.c_str()); #ifdef ART_TARGET_ANDROID
android_dlextinfo extinfo = {};
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; // Force-load, don't reuse handle // (open oat files multiple times). if (reservation != nullptr) { if (!reservation->IsValid()) {
*error_msg = StringPrintf("Invalid reservation for %s", elf_filename.c_str()); returnfalse;
}
extinfo.flags |= ANDROID_DLEXT_RESERVED_ADDRESS; // Use the reserved memory range.
extinfo.reserved_addr = reservation->Begin();
extinfo.reserved_size = reservation->Size();
}
if (strncmp(kAndroidArtApexDefaultPath,
absolute_path.c_str(), sizeof(kAndroidArtApexDefaultPath) - 1) != 0 ||
absolute_path.c_str()[sizeof(kAndroidArtApexDefaultPath) - 1] != '/') { // Use the system namespace for OAT files outside the ART APEX. Search // paths and links don't matter here, but permitted paths do, and the // system namespace is configured to allow loading from all appropriate // locations.
extinfo.flags |= ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = GetSystemLinkerNamespace();
}
dlopen_handle_ = android_dlopen_ext(absolute_path.c_str(), RTLD_NOW, &extinfo); if (reservation != nullptr && dlopen_handle_ != nullptr) { // Find used pages from the reservation. struct dl_iterate_context { staticint callback(dl_phdr_info* info, [[maybe_unused]] size_t size, void* data) { auto* context = reinterpret_cast<dl_iterate_context*>(data);
static_assert(std::is_same<Elf32_Half, Elf64_Half>::value, "Half must match"); using Elf_Half = Elf64_Half;
// See whether this callback corresponds to the file which we have just loaded.
uint8_t* reservation_begin = context->reservation->Begin(); bool contained_in_reservation = false; for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
size_t offset = static_cast<size_t>(vaddr - reservation_begin); if (offset < context->reservation->Size()) {
contained_in_reservation = true;
DCHECK_LE(memsz, context->reservation->Size() - offset);
} elseif (vaddr < reservation_begin) { // Check that there's no overlap with the reservation.
DCHECK_LE(memsz, static_cast<size_t>(reservation_begin - vaddr));
} break; // It is sufficient to check the first PT_LOAD header.
}
}
if (contained_in_reservation) { for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
size_t offset = static_cast<size_t>(vaddr - reservation_begin);
DCHECK_LT(offset, context->reservation->Size());
DCHECK_LE(memsz, context->reservation->Size() - offset);
context->max_size = std::max(context->max_size, offset + memsz);
}
}
return1; // Stop iteration and return 1 from dl_iterate_phdr.
} return0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
}
if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {
LOG(FATAL) << "Could not find the shared object mmapped to the reservation.";
UNREACHABLE();
}
// Take ownership of the memory used by the shared object. dlopen() does not assume // full ownership of this memory and dlclose() shall just remap it as zero pages with // PROT_NONE. We need to unmap the memory when destroying this oat file. // The reserved memory size is aligned up to kElfSegmentAlignment to ensure // that the next reserved area will be aligned to the value.
dlopen_mmaps_.push_back(reservation->TakeReservedMemory(
CondRoundUp<kPageSizeAgnostic>(context.max_size, kElfSegmentAlignment)));
} #else
static_assert(!kIsTargetBuild || kIsTargetLinux || kIsTargetFuchsia, "host_dlopen_handles_ will leak handles"); if (reservation != nullptr) {
*error_msg = StringPrintf("dlopen() into reserved memory is unsupported on host for '%s'.",
elf_filename.c_str()); returnfalse;
}
MutexLock mu(Thread::Current(), *Locks::host_dlopen_handles_lock_);
dlopen_handle_ = dlopen(absolute_path.c_str(), RTLD_NOW); if (dlopen_handle_ != nullptr) { if (!host_dlopen_handles_.insert(dlopen_handle_).second) {
dlclose(dlopen_handle_);
dlopen_handle_ = nullptr;
*error_msg = StringPrintf("host dlopen re-opened '%s'", elf_filename.c_str()); returnfalse;
}
} #endif// ART_TARGET_ANDROID
} if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror()); returnfalse;
} returntrue; #endif
}
context->shared_objects_seen++; if (context->shared_objects_seen < context->shared_objects_before) { // We haven't been called yet for anything we haven't seen before. Just continue. // Note: this is aggressively optimistic. If another thread was unloading a library, // we may miss out here. However, this does not happen often in practice. return0;
}
// See whether this callback corresponds to the file which we have just loaded. bool contains_begin = false; for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz; if (vaddr <= context->begin_ && context->begin_ < vaddr + memsz) {
contains_begin = true; break;
}
}
} // Add placeholder mmaps for this file. if (contains_begin) { for (Elf_Half i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type == PT_LOAD) {
uint8_t* vaddr = reinterpret_cast<uint8_t*>(info->dlpi_addr +
info->dlpi_phdr[i].p_vaddr);
size_t memsz = info->dlpi_phdr[i].p_memsz;
size_t name_size = strlen(info->dlpi_name) + 1u;
std::vector<char>* placeholder_maps_names = context->placeholder_maps_names_; // We must not allocate any memory in the callback, see b/156312036 . if (name_size < placeholder_maps_names->capacity() - placeholder_maps_names->size() &&
context->placeholder_maps_data_->size() <
context->placeholder_maps_data_->capacity()) {
placeholder_maps_names->insert(
placeholder_maps_names->end(), info->dlpi_name, info->dlpi_name + name_size); constchar* name =
&(*placeholder_maps_names)[placeholder_maps_names->size() - name_size];
context->placeholder_maps_data_->push_back({ name, vaddr, memsz });
}
context->num_placeholder_maps_ += 1u;
context->placeholder_maps_names_size_ += name_size;
}
} return1; // Stop iteration and return 1 from dl_iterate_phdr.
} return0; // Continue iteration and return 0 from dl_iterate_phdr when finished.
} const uint8_t* const begin_;
std::vector<PlaceholderMapData>* placeholder_maps_data_;
size_t num_placeholder_maps_;
std::vector<char>* placeholder_maps_names_;
size_t placeholder_maps_names_size_;
size_t shared_objects_before;
size_t shared_objects_seen;
};
// We must not allocate any memory in the callback, see b/156312036 . // Therefore we pre-allocate storage for the data we need for creating the placeholder maps.
std::vector<PlaceholderMapData> placeholder_maps_data;
placeholder_maps_data.reserve(32); // 32 should be enough. If not, we'll retry.
std::vector<char> placeholder_maps_names;
placeholder_maps_names.reserve(4 * KB); // 4KiB should be enough. If not, we'll retry.
if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) { // Hm. Maybe our optimization went wrong. Try another time with shared_objects_before == 0 // before giving up. This should be unusual.
VLOG(oat) << "Need a second run in PreSetup, didn't find with shared_objects_before="
<< shared_objects_before_;
DCHECK(placeholder_maps_data.empty());
DCHECK_EQ(context.num_placeholder_maps_, 0u);
DCHECK(placeholder_maps_names.empty());
DCHECK_EQ(context.placeholder_maps_names_size_, 0u);
context.shared_objects_before = 0u;
context.shared_objects_seen = 0u; if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) { // OK, give up and print an error.
PrintFileToLog("/proc/self/maps", android::base::LogSeverity::WARNING);
LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but cannot find its mmaps.";
}
}
if (placeholder_maps_data.size() < context.num_placeholder_maps_) { // Insufficient capacity. Reserve more space and retry.
placeholder_maps_data.clear();
placeholder_maps_data.reserve(context.num_placeholder_maps_);
context.num_placeholder_maps_ = 0u;
placeholder_maps_names.clear();
placeholder_maps_names.reserve(context.placeholder_maps_names_size_);
context.placeholder_maps_names_size_ = 0u;
context.shared_objects_before = 0u;
context.shared_objects_seen = 0u; bool success = (dl_iterate_phdr(dl_iterate_context::callback, &context) != 0);
CHECK(success);
}
// Mirrors the alignment in the Bionic's dlopen. Actually, ART's MemMap only requires 4096 byte // alignment, but we want to be more strict here, to reflect what the Bionic's dlopen would be // able to load. auto [file, start, length] = OS::OpenFileDirectlyOrFromZip(
elf_filename, kZipSeparator, /*alignment=*/MemMap::GetPageSize(), error_msg); if (file == nullptr) { returnfalse;
}
const uint8_t* ComputeElfBegin(std::string* error_msg) const override {
*error_msg = StringPrintf("Cannot get ELF begin because '%s' is not backed by an ELF file",
GetLocation().c_str()); return nullptr;
}
private:
OatHeader* oat_header_;
DISALLOW_COPY_AND_ASSIGN(OatFileBackedByVdex);
};
////////////////////////// // General OatFile code // //////////////////////////
// Check that the vdex file even exists, fast-fail. We don't check the odex // file as we use the absence of an odex file for test the functionality of // vdex-only. if (!OS::FileExists(vdex_filename.c_str())) {
*error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str()); return nullptr;
}
// Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is // disabled.
OatFile* with_dlopen = OatFileBase::OpenOatFile<DlOpenOatFile>(zip_fd,
vdex_filename,
oat_filename,
oat_location,
executable,
low_4gb,
dex_filenames,
dex_files,
reservation,
error_msg); if (with_dlopen != nullptr) { return with_dlopen;
} if (kPrintDlOpenErrorMessage) {
LOG(ERROR) << "Failed to dlopen: " << oat_filename << " with error " << *error_msg;
} // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: // // On target, dlopen may fail when compiling due to selinux restrictions on installd. // // We use our own ELF loader for Quick to deal with legacy apps that // open a generated dex file by name, remove the file, then open // another generated dex file with the same name. http://b/10614658 // // On host, dlopen is expected to fail when cross compiling, so fall back to ElfOatFile. // // // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN.
OatFile* with_internal = OatFileBase::OpenOatFile<ElfOatFile>(zip_fd,
vdex_filename,
oat_filename,
oat_location,
executable,
low_4gb,
dex_filenames,
dex_files,
reservation,
error_msg); return with_internal;
}
// Check if the dm file exists, to fail fast. The dm file contains the vdex that is essential for // using the odex in the sdm file. if (!OS::FileExists(dm_filename.c_str())) {
*error_msg =
ART_FORMAT("Not loading sdm file because dm file '{}' does not exist", dm_filename); return nullptr;
}
// Try dlopen first, as it is required for native debuggability. This will fail fast if dlopen is // disabled.
OatFile* with_dlopen = OatFileBase::OpenOatFileFromSdm<DlOpenOatFile>(
sdm_filename, sdc_filename, dm_filename, dex_filename, executable, error_msg); if (with_dlopen != nullptr) { return with_dlopen;
}
const OatDexFile* OatFile::GetOatDexFile(constchar* dex_location, std::string* error_msg) const { // NOTE: We assume here that the canonical location for a given dex_location never // changes. If it does (i.e. some symlink used by the filename changes) we may return // an incorrect OatDexFile. As long as we have a checksum to check, we shall return // an identical file or fail; otherwise we may see some unpredictable failures.
// TODO: Additional analysis of usage patterns to see if this can be simplified // without any performance loss, for example by not doing the first lock-free lookup.
const OatDexFile* oat_dex_file = nullptr;
std::string_view key(dex_location); // Try to find the key cheaply in the oat_dex_files_ map which holds dex locations // directly mentioned in the oat file and doesn't require locking. auto primary_it = oat_dex_files_.find(key); if (primary_it != oat_dex_files_.end()) {
oat_dex_file = primary_it->second;
DCHECK(oat_dex_file != nullptr);
} else { // This dex_location is not one of the dex locations directly mentioned in the // oat file. The correct lookup is via the canonical location but first see in // the secondary_oat_dex_files_ whether we've looked up this location before.
MutexLock mu(Thread::Current(), secondary_lookup_lock_); auto secondary_lb = secondary_oat_dex_files_.lower_bound(key); if (secondary_lb != secondary_oat_dex_files_.end() && key == secondary_lb->first) {
oat_dex_file = secondary_lb->second; // May be null.
} else { // We haven't seen this dex_location before, we must check the canonical location.
std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location); if (dex_canonical_location != dex_location) {
std::string_view canonical_key(dex_canonical_location); auto canonical_it = oat_dex_files_.find(canonical_key); if (canonical_it != oat_dex_files_.end()) {
oat_dex_file = canonical_it->second;
} // else keep null.
} // else keep null.
// Copy the key to the string_cache_ and store the result in secondary map.
string_cache_.emplace_back(key.data(), key.length());
std::string_view key_copy(string_cache_.back());
secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file);
}
}
if (oat_dex_file == nullptr) { if (error_msg != nullptr) {
std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
*error_msg = "Failed to find OatDexFile for DexFile " + std::string(dex_location)
+ " (canonical path " + dex_canonical_location + ") in OatFile " + GetLocation();
} return nullptr;
}
void OatDexFile::InitializeTypeLookupTable() { // Initialize TypeLookupTable. if (lookup_table_data_ != nullptr) { // Peek the number of classes from the DexFile. auto* dex_header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer_); const uint32_t num_class_defs = dex_header->class_defs_size_; if (lookup_table_data_ + TypeLookupTable::RawDataLength(num_class_defs) >
GetOatFile()->DexEnd()) {
LOG(WARNING) << "found truncated lookup table in " << dex_file_location_;
} else { const uint8_t* dex_data = dex_file_pointer_; // TODO: Clean this up to create the type lookup table after the dex file has been created? if (StandardDexFile::IsMagicValid(dex_header->magic_)) {
dex_data -= dex_header->HeaderOffset();
}
lookup_table_ = TypeLookupTable::Open(dex_data, lookup_table_data_, num_class_defs);
}
}
}
OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) { // Stripped-down OatDexFile only allowed in the compiler, the zygote, or the system server.
CHECK(Runtime::Current() == nullptr ||
Runtime::Current()->IsAotCompiler() ||
Runtime::Current()->IsZygote() ||
Runtime::Current()->IsSystemServer());
}
OatFile::OatClass OatDexFile::GetOatClass(uint16_t class_def_index) const { if (IsBackedByVdexOnly()) { // If there is only a vdex file, return that the class is not ready. The // caller will have to call `VdexFile::ComputeClassStatus` to compute the // actual class status, because we need to do the assignability type checks. return OatFile::OatClass(oat_file_,
ClassStatus::kNotReady, /* type= */ OatClassType::kNoneCompiled, /* num_methods= */ 0u, /* bitmap_pointer= */ nullptr, /* methods_pointer= */ nullptr);
}
const OatMethodOffsets* OatFile::OatClass::GetOatMethodOffsets(uint32_t method_index) const { // NOTE: We don't keep the number of methods for `kNoneCompiled` and cannot do // a bounds check for `method_index` in that case. if (methods_pointer_ == nullptr) {
CHECK_EQ(OatClassType::kNoneCompiled, type_); return nullptr;
}
CHECK_LT(method_index, num_methods_) << oat_file_->GetLocation();
size_t methods_pointer_index; if (bitmap_ == nullptr) {
CHECK_EQ(OatClassType::kAllCompiled, type_);
methods_pointer_index = method_index;
} else {
CHECK_EQ(OatClassType::kSomeCompiled, type_); if (!BitVector::IsBitSet(bitmap_, method_index)) { return nullptr;
}
size_t num_set_bits = BitVector::NumSetBits(bitmap_, method_index);
methods_pointer_index = num_set_bits;
} if (kIsDebugBuild) {
size_t size_until_end = dchecked_integral_cast<size_t>(
oat_file_->End() - reinterpret_cast<const uint8_t*>(methods_pointer_));
CHECK_LE(methods_pointer_index, size_until_end / sizeof(OatMethodOffsets))
<< oat_file_->GetLocation();
} const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index]; return &oat_method_offsets;
}
const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const { const OatMethodOffsets* oat_method_offsets = GetOatMethodOffsets(method_index); if (oat_method_offsets == nullptr) { return OatMethod(nullptr, 0);
} if (oat_file_->IsExecutable() ||
Runtime::Current() == nullptr || // This case applies for oatdump.
Runtime::Current()->IsAotCompiler()) { return OatMethod(oat_file_->Begin(), oat_method_offsets->code_offset_);
} // We aren't allowed to use the compiled code. We just force it down the interpreted / jit // version. return OatMethod(oat_file_->Begin(), 0);
}
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.