tools/oatmeal/dump-oat.cpp (2,798 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ // Code for parsing and building OAT files for multiple android versions. See // OatFile::build and OatFile::parse, below. #if __ANDROID__ #include <museum/5.0.0/bionic/libc/android/legacy_stdlib_inlines.h> #include <museum/5.0.0/bionic/libc/ctype.h> #include <museum/5.0.0/bionic/libc/errno.h> #include <museum/5.0.0/bionic/libc/locale.h> #include <museum/5.0.0/bionic/libc/math.h> #include <museum/5.0.0/bionic/libc/pthread.h> #include <museum/5.0.0/bionic/libc/stdlib.h> #include <museum/5.0.0/bionic/libc/sys/stat.h> #include <museum/5.0.0/bionic/libc/wchar.h> #include <museum/5.0.0/bionic/libc/wctype.h> #include <museum/5.0.0/external/libcxx/support/android/locale_bionic.h> #endif // __ANDROID__ #include "OatmealUtil.h" #include "QuickData.h" #include "Util.h" #include "dex.h" #include "dump-oat.h" #include "elf-writer.h" #include "memory-accounter.h" #include "vdex.h" #include <algorithm> #include <cerrno> #include <cstdint> #include <cstdio> #include <cstring> #include <memory> #include <string> #include <sys/stat.h> #include <utility> #include <vector> #define PACK __attribute__((packed)) #define READ_WORD(dest, ptr) \ do { \ cur_ma()->memcpyAndMark((dest), ptr, 4); \ ptr += 4; \ } while (false); namespace { // "86827de6f1ef3407f8dc98b76382d3a6e0759ab3" is the SHA1 digest for // 'created_by_oatmeal'. const char* kCreatedByOatmeal = "86827de6f1ef3407f8dc98b76382d3a6e0759ab3"; VdexVersion vdexVersion(OatVersion oat_version) { switch (oat_version) { case OatVersion::V_124: return VdexVersion::V_006; case OatVersion::V_131: return VdexVersion::V_010; default: return VdexVersion::UNKNOWN; } } OatVersion versionInt(const std::string& version_str) { if (version_str == "039") { return OatVersion::V_039; } else if (version_str == "045") { return OatVersion::V_045; } else if (version_str == "064") { return OatVersion::V_064; } else if (version_str == "067") { return OatVersion::V_067; } else if (version_str == "079") { return OatVersion::V_079; } else if (version_str == "088") { return OatVersion::V_088; } else if (version_str == "124") { return OatVersion::V_124; } else if (version_str == "131") { return OatVersion::V_131; } else { CHECK(false, "Bad version %s", version_str.c_str()); } return OatVersion::UNKNOWN; } struct PACK ImageInfo_064 { int32_t patch_delta = 0; uint32_t oat_checksum = 0; uint32_t data_begin = 0; ImageInfo_064() = default; ImageInfo_064(int32_t pd, uint32_t oc, uint32_t db) : patch_delta(pd), oat_checksum(oc), data_begin(db) {} }; struct PACK ArtImageHeader { enum class Version { V_009 = 0x00393030, V_012 = 0x00323130, V_017 = 0x00373130, }; uint32_t magic; uint32_t version; uint32_t image_begin; uint32_t image_size; // next two fields present in versions 012 // not present in versions 017 uint32_t image_bitmap_offset; uint32_t image_bitmap_size; uint32_t oat_checksum; uint32_t oat_file_begin; uint32_t oat_data_begin; uint32_t oat_data_end; uint32_t oat_file_end; int32_t patch_delta; uint32_t image_roots; uint32_t pointer_size; uint32_t compile_pic; static std::unique_ptr<ArtImageHeader> parse(FileHandle& fh) { constexpr auto size = sizeof(ArtImageHeader); auto buf = std::make_unique<char[]>(size); auto ptr = buf.get(); auto num_read = fh.fread(ptr, size, 1); if (num_read != 1) { return nullptr; } auto ret = std::make_unique<ArtImageHeader>(); READ_WORD(&ret->magic, ptr); READ_WORD(&ret->version, ptr); READ_WORD(&ret->image_begin, ptr); READ_WORD(&ret->image_size, ptr); switch (static_cast<Version>(ret->version)) { case Version::V_009: case Version::V_012: READ_WORD(&ret->image_bitmap_offset, ptr); READ_WORD(&ret->image_bitmap_size, ptr); break; case Version::V_017: break; } READ_WORD(&ret->oat_checksum, ptr); READ_WORD(&ret->oat_file_begin, ptr); READ_WORD(&ret->oat_data_begin, ptr); READ_WORD(&ret->oat_data_end, ptr); READ_WORD(&ret->oat_file_end, ptr); READ_WORD(&ret->patch_delta, ptr); READ_WORD(&ret->image_roots, ptr); READ_WORD(&ret->pointer_size, ptr); READ_WORD(&ret->compile_pic, ptr); return ret; } }; struct PACK OatHeader_Common { uint32_t magic = 0; uint32_t version = 0; uint32_t adler32_checksum = 0; static OatHeader_Common parse(ConstBuffer buf) { OatHeader_Common header; CHECK(buf.len >= sizeof(OatHeader_Common)); cur_ma()->memcpyAndMark(&header, buf.ptr, sizeof(OatHeader_Common)); return header; } void print() { // char magic_str[5] = {}; // char version_str[5] = {}; // memcpy(magic_str, &magic, 3); // magic has a newline character at idx 4. // memcpy(version_str, &version, 4); printf( "OatHeader_Common: {magic: 0x%08x, \ version: 0x%08x, \ checksum: 0x%08x}\n", magic, version, adler32_checksum); } }; struct PACK OatHeader { OatHeader_Common common; InstructionSet instruction_set = InstructionSet::kNone; uint32_t instruction_set_features_bitmap = 0; uint32_t dex_file_count = 0; uint32_t oat_dex_files_offset = 0; // Only on API 27 uint32_t executable_offset = 0; uint32_t interpreter_to_interpreter_bridge_offset = 0; uint32_t interpreter_to_compiled_code_bridge_offset = 0; uint32_t jni_dlsym_lookup_offset = 0; // These three fields are not present in version 064 and up. uint32_t portable_imt_conflict_trampoline_offset = 0; uint32_t portable_resolution_trampoline_offset = 0; uint32_t portable_to_interpreter_bridge_offset = 0; uint32_t quick_generic_jni_trampoline_offset = 0; uint32_t quick_imt_conflict_trampoline_offset = 0; uint32_t quick_resolution_trampoline_offset = 0; uint32_t quick_to_interpreter_bridge_offset = 0; int32_t image_patch_delta = 0; uint32_t image_file_location_oat_checksum = 0; uint32_t image_file_location_oat_data_begin = 0; uint32_t key_value_store_size = 0; // note: variable width data at end uint8_t key_value_store[0]; static size_t size(OatVersion version) { if (version == OatVersion::V_039 || version == OatVersion::V_045) { // Subtract the size of oat_dex_files_offset which is only present // in API 27. return sizeof(OatHeader) - 1 * sizeof(uint32_t); } else if (version == OatVersion::V_131) { // Minus the 3 fields that are not present in 064 and above. return sizeof(OatHeader) - 3 * sizeof(uint32_t); } else { // Minus the 3 fields not in 064 and oat_dex_files_offset which only // shows up in 131. return sizeof(OatHeader) - 4 * sizeof(uint32_t); } } size_t size() const { return OatHeader::size(static_cast<OatVersion>(common.version)); } static OatHeader parse(ConstBuffer buf) { OatHeader header; CHECK(buf.len >= sizeof(OatHeader_Common)); cur_ma()->memcpyAndMark(&header, buf.ptr, sizeof(OatHeader_Common)); auto ptr = buf.ptr + sizeof(OatHeader_Common); READ_WORD(&header.instruction_set, ptr); READ_WORD(&header.instruction_set_features_bitmap, ptr); READ_WORD(&header.dex_file_count, ptr); if (header.common.version == static_cast<uint32_t>(OatVersion::V_131)) { READ_WORD(&header.oat_dex_files_offset, ptr); } READ_WORD(&header.executable_offset, ptr); READ_WORD(&header.interpreter_to_interpreter_bridge_offset, ptr); READ_WORD(&header.interpreter_to_compiled_code_bridge_offset, ptr); READ_WORD(&header.jni_dlsym_lookup_offset, ptr); // These three fields are not present in version 064 and up. if (header.common.version == static_cast<uint32_t>(OatVersion::V_045) || header.common.version == static_cast<uint32_t>(OatVersion::V_039)) { READ_WORD(&header.portable_imt_conflict_trampoline_offset, ptr); READ_WORD(&header.portable_resolution_trampoline_offset, ptr); READ_WORD(&header.portable_to_interpreter_bridge_offset, ptr); } READ_WORD(&header.quick_generic_jni_trampoline_offset, ptr); READ_WORD(&header.quick_imt_conflict_trampoline_offset, ptr); READ_WORD(&header.quick_resolution_trampoline_offset, ptr); READ_WORD(&header.quick_to_interpreter_bridge_offset, ptr); READ_WORD(&header.image_patch_delta, ptr); READ_WORD(&header.image_file_location_oat_checksum, ptr); READ_WORD(&header.image_file_location_oat_data_begin, ptr); READ_WORD(&header.key_value_store_size, ptr); CHECK(header.common.magic == kOatMagicNum); return header; } void write(FileHandle& fh) { write_obj(fh, common); write_word(fh, static_cast<uint32_t>(instruction_set)); write_word(fh, instruction_set_features_bitmap); write_word(fh, dex_file_count); if (common.version == static_cast<uint32_t>(OatVersion::V_131)) { write_word(fh, oat_dex_files_offset); } write_word(fh, executable_offset); write_word(fh, interpreter_to_interpreter_bridge_offset); write_word(fh, interpreter_to_compiled_code_bridge_offset); write_word(fh, jni_dlsym_lookup_offset); // These three fields are not present in version 064 and up. if (common.version == static_cast<uint32_t>(OatVersion::V_045) || common.version == static_cast<uint32_t>(OatVersion::V_039)) { write_word(fh, portable_imt_conflict_trampoline_offset); write_word(fh, portable_resolution_trampoline_offset); write_word(fh, portable_to_interpreter_bridge_offset); } write_word(fh, quick_generic_jni_trampoline_offset); write_word(fh, quick_imt_conflict_trampoline_offset); write_word(fh, quick_resolution_trampoline_offset); write_word(fh, quick_to_interpreter_bridge_offset); write_word(fh, image_patch_delta); write_word(fh, image_file_location_oat_checksum); write_word(fh, image_file_location_oat_data_begin); write_word(fh, key_value_store_size); } void print() { printf( "OatHeader: {magic: 0x%08x, \ version: 0x%08x, \ checksum: 0x%08x, \ isa: %s, \ isa_features_bitmap: 0x%08x, \ dex_file_count: 0x%08x, \ executable_offset: 0x%08x, \ interpreter_to_interpreter_bridge_offset: 0x%08x, \ interpreter_to_compiled_code_bridge_offset: 0x%08x, \ jni_dlsym_lookup_offset: 0x%08x", common.magic, common.version, common.adler32_checksum, instruction_set_str(instruction_set), instruction_set_features_bitmap, dex_file_count, executable_offset, interpreter_to_interpreter_bridge_offset, interpreter_to_compiled_code_bridge_offset, jni_dlsym_lookup_offset); if (common.version == static_cast<uint32_t>(OatVersion::V_045) || common.version == static_cast<uint32_t>(OatVersion::V_039)) { printf( "portable_imt_conflict_trampoline_offset: 0x%08x, \ portable_resolution_trampoline_offset: 0x%08x, \ portable_to_interpreter_bridge_offset: 0x%08x", portable_imt_conflict_trampoline_offset, portable_resolution_trampoline_offset, portable_to_interpreter_bridge_offset); } printf( "quick_generic_jni_trampoline_offset: 0x%08x, \ quick_imt_conflict_trampoline_offset: 0x%08x, \ quick_resolution_trampoline_offset: 0x%08x, \ quick_to_interpreter_bridge_offset: 0x%08x, \ image_patch_delta: 0x%08x, \ image_file_location_oat_checksum: 0x%08x, \ image_file_location_oat_data_begin: 0x%08x, \ key_value_store_size: 0x%08x}\n", quick_generic_jni_trampoline_offset, quick_imt_conflict_trampoline_offset, quick_resolution_trampoline_offset, quick_to_interpreter_bridge_offset, image_patch_delta, image_file_location_oat_checksum, image_file_location_oat_data_begin, key_value_store_size); } }; // The oat file key-value store is a section of the oat file containing // zero or more pairs of null-terminated strings. class KeyValueStore { public: using KeyValue = std::pair<std::string, std::string>; explicit KeyValueStore(ConstBuffer buf) { cur_ma()->markBufferConsumed(buf); int remaining = buf.len; size_t next = 0; while (remaining > 0) { KeyValue kv; // key { auto len = strnlen(&buf[next], remaining) + 1; kv.first = std::string(&buf[next], len - 1); next += len; remaining -= len; } if (remaining <= 0) { break; } // value { auto len = strnlen(&buf[next], remaining) + 1; kv.second = std::string(&buf[next], len - 1); next += len; remaining -= len; } kv_pairs_.push_back(std::move(kv)); } } void print() const { for (const auto& e : kv_pairs_) { printf("KeyValueStore: {%s: %s}\n", e.first.c_str(), e.second.c_str()); } } static void write(FileHandle& fh, const std::vector<KeyValue>& kv_pairs) { for (const auto& e : kv_pairs) { write_str_and_null(fh, e.first); write_str_and_null(fh, e.second); } } static uint32_t compute_size(const std::vector<KeyValue>& kv_pairs) { uint32_t ret = 0; for (const auto& e : kv_pairs) { ret += e.first.size() + 1; ret += e.second.size() + 1; } return ret; } bool has_key(const std::string& key) const { for (const auto& kv : kv_pairs_) { if (kv.first == key) { return true; } } return false; } // return value remains valid as long as *this isn't destroyed. const char* get(const std::string& key) const { for (const auto& kv : kv_pairs_) { if (kv.first == key) { return kv.second.c_str(); } } return nullptr; } private: std::vector<KeyValue> kv_pairs_; }; // DexIdBufs handles looking up class names in dex files within in-memory oat // files. class DexIdBufs { public: // Note: DexIdBufs must not outlive the memory wrapped by oat_buf. DexIdBufs(ConstBuffer oat_buf, uint32_t dex_offset, const DexFileHeader& header) { dex_buf_ = oat_buf.slice(dex_offset); auto class_defs_buf = dex_buf_.slice(header.class_defs_off); auto type_ids_buf = dex_buf_.slice(header.type_ids_off); auto string_ids_buf = dex_buf_.slice(header.string_ids_off); auto method_ids_buf = dex_buf_.slice(header.method_ids_off); // We memcpy into new buffers since the data in the dex may not be aligned. class_defs_.reset(new DexClassDef[header.class_defs_size]); type_ids_.reset(new uint32_t[header.type_ids_size]); string_ids_.reset(new uint32_t[header.string_ids_size]); memcpy(class_defs_.get(), class_defs_buf.ptr, header.class_defs_size * sizeof(DexClassDef)); memcpy(type_ids_.get(), type_ids_buf.ptr, header.type_ids_size * sizeof(uint32_t)); memcpy(string_ids_.get(), string_ids_buf.ptr, header.string_ids_size * sizeof(uint32_t)); // note: method ids are indexed by type, not class, hence must be size of // type_ids_size class_method_count_.resize(header.type_ids_size, 0); auto method_ids = std::make_unique<MethodId[]>(header.method_ids_size); memcpy(method_ids.get(), method_ids_buf.ptr, header.method_ids_size * sizeof(MethodId)); for (unsigned int i = 0; i < header.method_ids_size; i++) { class_method_count_.at(method_ids[i].class_idx)++; } } int get_num_methods(int i) const { return class_method_count_[class_defs_[i].class_idx]; } std::string get_class_name(int i) const { const auto class_idx = class_defs_[i].class_idx; const auto string_id = type_ids_[class_idx]; const auto string_offset = string_ids_[string_id]; auto string_buf = dex_buf_.slice(string_offset); char* ptr = const_cast<char*>(string_buf.ptr); const auto str_size = read_uleb128(&ptr) + 1; return std::string(ptr, str_size); } private: ConstBuffer dex_buf_; std::unique_ptr<DexClassDef[]> class_defs_; std::unique_ptr<uint32_t[]> type_ids_; std::unique_ptr<uint32_t[]> string_ids_; std::vector<int> class_method_count_; }; // Class meta data for all the classes that appear in the dex files. // ClassOffsets[0]...ClassOffsets[D] and OatClass[0]..OatClass[C] sections // - DexFileListing (OatDexFile[0]...OatDexFile[D]) specifies the beginning of // the ClassOffsets for each dex file. // - The class listing for a dex file is doubly indirect. It consists of an // array of offsets, whose length is specified by // DexFileHeader::class_defs_size. Each offset in that array points to // a single ClassInfo struct for that class. class OatClasses { public: enum class Status { kStatusRetired = -2, kStatusError = -1, kStatusNotReady = 0, kStatusIdx = 1, kStatusLoaded = 2, kStatusResolving = 3, kStatusResolved = 4, kStatusVerifying = 5, kStatusRetryVerificationAtRuntime = 6, kStatusVerifyingAtRuntime = 7, kStatusVerified = 8, kStatusInitializing = 9, kStatusInitialized = 10, kStatusMax = 11, }; enum class Type { kOatClassAllCompiled = 0, kOatClassSomeCompiled = 1, kOatClassNoneCompiled = 2, kOatClassMax = 3, }; static const char* statusStr(Status status) { switch (status) { case Status::kStatusRetired: return "kStatusRetired"; case Status::kStatusError: return "kStatusError"; case Status::kStatusNotReady: return "kStatusNotReady"; case Status::kStatusIdx: return "kStatusIdx"; case Status::kStatusLoaded: return "kStatusLoaded"; case Status::kStatusResolving: return "kStatusResolving"; case Status::kStatusResolved: return "kStatusResolved"; case Status::kStatusVerifying: return "kStatusVerifying"; case Status::kStatusRetryVerificationAtRuntime: return "kStatusRetryVerificationAtRuntime"; case Status::kStatusVerifyingAtRuntime: return "kStatusVerifyingAtRuntime"; case Status::kStatusVerified: return "kStatusVerified"; case Status::kStatusInitializing: return "kStatusInitializing"; case Status::kStatusInitialized: return "kStatusInitialized"; case Status::kStatusMax: return "kStatusMax"; default: return "<UNKNOWN>"; } } static const char* statusStr(int status) { return statusStr(static_cast<Status>(status)); } static const char* shortStatusStr(Status status) { switch (status) { case Status::kStatusRetired: return "O"; case Status::kStatusError: return "E"; case Status::kStatusNotReady: return "N"; case Status::kStatusIdx: return "I"; case Status::kStatusLoaded: return "L"; case Status::kStatusResolving: return "r"; case Status::kStatusResolved: return "R"; case Status::kStatusVerifying: return "v"; case Status::kStatusRetryVerificationAtRuntime: return "v"; case Status::kStatusVerifyingAtRuntime: return "v"; case Status::kStatusVerified: return "V"; case Status::kStatusInitializing: return "i"; case Status::kStatusInitialized: return "I"; case Status::kStatusMax: return "M"; default: return "?"; } } static const char* typeStr(Type type) { switch (type) { case Type::kOatClassAllCompiled: return "kOatClassAllCompiled"; case Type::kOatClassSomeCompiled: return "kOatClassSomeCompiled"; case Type::kOatClassNoneCompiled: return "kOatClassNoneCompiled"; case Type::kOatClassMax: return "kOatClassMax"; default: return "<UKNOWN>"; } } static const char* shortTypeStr(Type type) { switch (type) { case Type::kOatClassAllCompiled: return "C"; case Type::kOatClassSomeCompiled: return "c"; case Type::kOatClassNoneCompiled: return "n"; case Type::kOatClassMax: return "M"; default: return "?"; } } // Note that this only handles uncompiled classes. Compiled classes // additionally contain a bitmap for each method, along with a field // specifying the length of the bitmap. struct PACK ClassInfo { ClassInfo() = default; ClassInfo(Status s, Type t) : status(static_cast<int16_t>(s)), type(static_cast<uint16_t>(t)) {} int16_t status = 0; uint16_t type = 0; }; protected: OatClasses() = default; }; class DexFileListing { public: struct DexFile { DexFile() = default; DexFile(const std::string& location_, uint32_t location_checksum_, uint32_t file_offset_) : location(location_), location_checksum(location_checksum_), file_offset(file_offset_) {} std::string location; uint32_t location_checksum; uint32_t file_offset; }; virtual std::vector<uint32_t> dex_file_offsets() const = 0; virtual ~DexFileListing() = default; }; // Dex File listing for OAT versions 079, 088. // // Meta data about dex files, comes immediately after the KeyValueStore // OatHeader::dex_file_count specifies how many entries there are in the // listing. // // Each listing consists of: // // location_length: 4 unsigned bytes. // location: un-terminated character string, length specified by // location_length // location_checksum: 4 unsigned bytes, checksum of location. // file_offset: 4 unsigned bytes, offset from beginning of OAT file // where the specified dex file begins. // classes_offset: 4 unsigned bytes, offset from beginning of OAT file // where class metadata listing (OatClasses) for this // dex file begins. // lookup_table_offset: 4 unsigned bytes, offset from beginning of OAT file // where the class lookup table (LookupTables) for this // dex file begins. class DexFileListing_079 : public DexFileListing { public: MOVABLE(DexFileListing_079); struct DexFile_079 : public DexFile { DexFile_079() = default; DexFile_079(const std::string& location_, uint32_t location_checksum_, uint32_t file_offset_, uint32_t num_classes_, uint32_t classes_offset_, uint32_t lookup_table_offset_) : DexFile(location_, location_checksum_, file_offset_), num_classes(num_classes_), classes_offset(classes_offset_), lookup_table_offset(lookup_table_offset_) {} uint32_t num_classes; uint32_t classes_offset; uint32_t lookup_table_offset; }; DexFileListing_079(int numDexFiles, ConstBuffer buf) { auto ptr = buf.ptr; while (numDexFiles > 0) { numDexFiles--; DexFile_079 file; uint32_t location_len = 0; READ_WORD(&location_len, ptr); file.location = std::string(ptr, location_len); cur_ma()->markRangeConsumed(ptr, location_len); ptr += location_len; READ_WORD(&file.location_checksum, ptr); READ_WORD(&file.file_offset, ptr); READ_WORD(&file.classes_offset, ptr); READ_WORD(&file.lookup_table_offset, ptr); dex_files_.push_back(file); } } void print() { size_t i = 0; for (const auto& e : dex_files_) { printf( "OatDexFile[%zu]: {location: %s, \ location_checksum: 0x%08x, \ file_offset: 0x%08x, \ classes_offset: 0x%08x, \ lookup_table_offset: 0x%08x}\n", i, e.location.c_str(), e.location_checksum, e.file_offset, e.classes_offset, e.lookup_table_offset); i++; } } const std::vector<DexFile_079>& dex_files() const { return dex_files_; } std::vector<uint32_t> dex_file_offsets() const override { std::vector<uint32_t> ret; for (const auto& f : dex_files_) { ret.push_back(f.file_offset); } return ret; } static uint32_t compute_size(const std::vector<DexInput>& dex_input, bool /*samsung_mode*/) { // Locations are *not* null terminated. const auto num_files = dex_input.size(); uint32_t total_file_location_size = 0; for (const auto& e : dex_input) { total_file_location_size += e.location.size(); } return total_file_location_size + num_files * sizeof(uint32_t) + // location len num_files * sizeof(uint32_t) + // location checksum num_files * sizeof(uint32_t) + // file offset num_files * sizeof(uint32_t) + // classes offset num_files * sizeof(uint32_t); // lookup_table_offset } static std::vector<DexFile_079> build(const std::vector<DexInput>& dexes, uint32_t& next_offset, bool samsung_mode); static void write( FileHandle& fh, const std::vector<DexFileListing_079::DexFile_079>& dex_files, bool /*samsung_mode*/) { for (const auto& file : dex_files) { uint32_t location_len = file.location.size(); write_word(fh, location_len); ConstBuffer location{file.location.c_str(), location_len}; // Locations are *not* null terminated. write_buf(fh, location); write_word(fh, file.location_checksum); write_word(fh, file.file_offset); write_word(fh, file.classes_offset); write_word(fh, file.lookup_table_offset); } } private: std::vector<DexFile_079> dex_files_; }; class DexFileListing_124 : public DexFileListing_079 { public: using DexFile_124 = DexFile_079; MOVABLE(DexFileListing_124); DexFileListing_124(int numDexFiles, ConstBuffer buf) : DexFileListing_079(numDexFiles, buf) { CHECK(numDexFiles <= 1, "For V124/V131 we only support one dex per odex/vdex pair"); } static std::vector<DexFile_124> build(const std::vector<DexInput>& dexes, uint32_t& next_offset, bool samsung_mode); }; class DexFileListing_131 : public DexFileListing_124 { public: struct DexFile_131 : public DexFile_124 { DexFile_131() = default; DexFile_131(const std::string& location_, uint32_t location_checksum_, uint32_t file_offset_, uint32_t num_classes_, uint32_t classes_offset_, uint32_t lookup_table_offset_, uint32_t dex_layout_sections_offset_, uint32_t method_bss_mapping_offset_) : DexFile_124(location_, location_checksum_, file_offset_, num_classes_, classes_offset_, lookup_table_offset_), dex_layout_sections_offset(dex_layout_sections_offset_), method_bss_mapping_offset(method_bss_mapping_offset_) {} uint32_t dex_layout_sections_offset; uint32_t method_bss_mapping_offset; }; MOVABLE(DexFileListing_131); DexFileListing_131(int numDexFiles, ConstBuffer buf) : DexFileListing_124(numDexFiles, buf) { CHECK(numDexFiles <= 1, "For V124/V131 we only support one dex per odex/vdex pair"); } static uint32_t compute_size(const std::vector<DexInput>& dex_input, bool /*samsung_mode*/) { // Locations are *not* null terminated. const auto num_files = dex_input.size(); uint32_t total_file_location_size = 0; for (const auto& e : dex_input) { total_file_location_size += e.location.size(); } return num_files * sizeof(uint32_t) + // location len total_file_location_size + num_files * sizeof(uint32_t) + // location checksum num_files * sizeof(uint32_t) + // dex file offset num_files * sizeof(uint32_t) + // classes offset num_files * sizeof(uint32_t) + // lookup_table_offset num_files * sizeof(uint32_t) + // dex_layout_sections_offset num_files * sizeof(uint32_t); // method_bss_mapping_offset } static std::vector<DexFile_131> build(const std::vector<DexInput>& dexes, uint32_t& next_offset, bool samsung_mode); static void write( FileHandle& fh, const std::vector<DexFileListing_131::DexFile_131>& dex_files, bool /*samsung_mode*/) { for (const auto& file : dex_files) { uint32_t location_len = file.location.size(); write_word(fh, location_len); ConstBuffer location{file.location.c_str(), location_len}; // Locations are *not* null terminated. write_buf(fh, location); write_word(fh, file.location_checksum); write_word(fh, file.file_offset); write_word(fh, file.classes_offset); write_word(fh, file.lookup_table_offset); write_word(fh, file.dex_layout_sections_offset); write_word(fh, file.method_bss_mapping_offset); #ifdef DEBUG_LOG printf( "WRITING DexFileListing_131: \ location_len: %u\ location: %s\ location_checksum: %04x\ file_offset: %u\ classes_offset: %u\ lookup_table_offset: %u\ dex_layout_sections_offset: %u\ method_bss_mapping_offset: %u\n", location_len, file.location.c_str(), file.location_checksum, file.file_offset, file.classes_offset, file.lookup_table_offset, file.dex_layout_sections_offset, file.method_bss_mapping_offset); #endif } } }; // Dex File listing for OAT versions 064 and 045. // // Meta data about dex files, comes immediately after the KeyValueStore // OatHeader::dex_file_count specifies how many entries there are in the // listing. // // Each listing consists of: // // location_length: 4 unsigned bytes. // location: un-terminated character string, length specified by // location_length // location_checksum: 4 unsigned bytes, checksum of location. // file_offset: 4 unsigned bytes, offset from beginning of OAT file // where the specified dex file begins. // classes: Variable length table of offsets pointing to class // status information. Length depends on the number of // classes in the dex file. // // The offsets in `classes` point to ClassInfo structs. If the value a // ClassInfo's type field is kOatClassSomeCompiled, then the ClassInfo is // followed by: // // - 4 bytes containing a bitmask size. // - N bytes of bitmask, where N is specified in the previous field. // - M 4 byte method pointers, where M is equal to the total number of set // bits in the bitmask. // // If the type field is kOatClassAllCompiled, then the ClassInfo is followed by // - M 4 byte methods pointers, where M is the number of methods in the given // class. // // Otherwise, there is no additional data after ClassInfo. class DexFileListing_064 : public DexFileListing { public: MOVABLE(DexFileListing_064); using ClassInfo = OatClasses::ClassInfo; struct DexFile_064 : public DexFile { DexFile_064() = default; DexFile_064(const std::string& location_, uint32_t location_checksum_, uint32_t file_offset_, uint32_t lookup_table_offset_, const std::vector<uint32_t>& class_offsets_, const std::vector<ClassInfo>& class_info_) : DexFile(location_, location_checksum_, file_offset_), lookup_table_offset(lookup_table_offset_), class_offsets(class_offsets_), class_info(class_info_) {} uint32_t lookup_table_offset; std::vector<uint32_t> class_offsets; std::vector<ClassInfo> class_info; std::vector<std::string> class_names; }; DexFileListing_064(bool dex_files_only, OatVersion version, int numDexFiles, ConstBuffer buf, ConstBuffer oat_buf) { auto oat_method_offset_size = 0; if (version == OatVersion::V_039) { // http://androidxref.com/5.0.0_r2/xref/art/runtime/oat.h#161 oat_method_offset_size = 8; } else if (version == OatVersion::V_045 || version == OatVersion::V_064 || version == OatVersion::V_067) { // http://androidxref.com/5.1.1_r6/xref/art/runtime/oat.h#163 oat_method_offset_size = 4; } else { CHECK(false, "Invalid oat version for DexFileListing_064"); } auto ptr = buf.ptr; while (numDexFiles > 0) { numDexFiles--; DexFile_064 file; uint32_t location_len = 0; READ_WORD(&location_len, ptr); file.location = std::string(ptr, location_len); cur_ma()->markRangeConsumed(ptr, location_len); ptr += location_len; READ_WORD(&file.location_checksum, ptr); READ_WORD(&file.file_offset, ptr); // Samsung has an extra field here, which is the offset to their custom // type lookup table. It comes before the dex, whereas the class info // tables come after the dex, so we can always detect this field based on // comparing it to the dex file offset. uint32_t next_word; memcpy(&next_word, ptr, 4); if (next_word < file.file_offset) { is_samsung_ = true; READ_WORD(&file.lookup_table_offset, ptr); } else { file.lookup_table_offset = 0; } auto dex_header = DexFileHeader::parse(oat_buf.slice(file.file_offset)); const auto num_classes = dex_header.class_defs_size; if (!dex_files_only) { file.class_info.reserve(num_classes); DexIdBufs id_bufs(oat_buf, file.file_offset, dex_header); for (unsigned int i = 0; i < num_classes; i++) { uint32_t class_info_offset; READ_WORD(&class_info_offset, ptr); ClassInfo class_info; cur_ma()->memcpyAndMark( &class_info, oat_buf.ptr + class_info_offset, sizeof(ClassInfo)); // Note: So far I haven't found this pattern in version 064, so I'm // not 100% sure this will work for 064. It definitely works for 045, // where this pattern appears to occur more frequently. if (class_info.type == static_cast<uint16_t>(OatClasses::Type::kOatClassSomeCompiled)) { auto bitmap_size_ptr = oat_buf.ptr + class_info_offset + sizeof(ClassInfo); uint32_t bitmap_size = 0; cur_ma()->memcpyAndMark( &bitmap_size, bitmap_size_ptr, sizeof(uint32_t)); auto bitmap_ptr = bitmap_size_ptr + sizeof(uint32_t); cur_ma()->markRangeConsumed(bitmap_ptr, bitmap_size); int method_count = 0; for (unsigned int j = 0; j < (bitmap_size / 4); j++) { uint32_t bitmap_element = 0; READ_WORD(&bitmap_element, bitmap_ptr); method_count += countSetBits(bitmap_element); } auto methods_ptr = bitmap_ptr; cur_ma()->markRangeConsumed(methods_ptr, method_count * oat_method_offset_size); } else if (class_info.type == static_cast<uint16_t>( OatClasses::Type::kOatClassAllCompiled)) { auto method_count = id_bufs.get_num_methods(i); auto methods_ptr = oat_buf.ptr + class_info_offset + sizeof(ClassInfo); cur_ma()->markRangeConsumed(methods_ptr, method_count * oat_method_offset_size); } file.class_info.push_back(class_info); file.class_names.push_back(id_bufs.get_class_name(i)); } } else { // must consume the class info. for (unsigned int i = 0; i < num_classes; i++) { uint32_t class_info_offset; READ_WORD(&class_info_offset, ptr); } } dex_files_.push_back(file); } } void print() { for (const auto& e : dex_files_) { printf(" {\n"); printf(" location: %s\n", e.location.c_str()); printf(" location_checksum: 0x%08x\n", e.location_checksum); printf(" file_offset: 0x%08x\n", e.file_offset); printf(" }\n"); } } void print_classes() { for (const auto& e : dex_files_) { printf(" { Classes for dex %s\n", e.location.c_str()); int count = 0; for (const auto& info : e.class_info) { if (count == 0) { printf(" "); } printf( "%s%s ", OatClasses::shortStatusStr( static_cast<OatClasses::Status>(info.status)), OatClasses::shortTypeStr(static_cast<OatClasses::Type>(info.type))); count++; if (count >= 32) { printf("\n"); count = 0; } } printf(" }\n"); } } void print_unverified_classes() { printf("unverified classes:\n"); for (const auto& e : dex_files_) { printf(" %s\n", e.location.c_str()); foreach_pair( e.class_info, e.class_names, [&](const ClassInfo& info, const std::string& name) { if (info.status < static_cast<int>(OatClasses::Status::kStatusVerified)) { printf(" %s unverified (status: %s)\n", name.c_str(), OatClasses::statusStr(info.status)); } }); } } std::vector<uint32_t> dex_file_offsets() const override { std::vector<uint32_t> ret; for (const auto& f : dex_files_) { ret.push_back(f.file_offset); } return ret; } const std::vector<DexFile_064>& dex_files() const { return dex_files_; } static uint32_t compute_size(const std::vector<DexInput>& dex_input, bool samsung_mode) { // Locations are *not* null terminated. const auto num_files = dex_input.size(); uint32_t total_file_location_size = 0; uint32_t total_class_data_size = 0; for (const auto& e : dex_input) { total_file_location_size += e.location.size(); auto dex_fh = FileHandle(fopen(e.filename.c_str(), "r")); CHECK(dex_fh.get() != nullptr); auto file_size = get_filesize(dex_fh); CHECK(file_size >= sizeof(DexFileHeader)); // read the header to get the count of classes. DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); total_class_data_size += header.class_defs_size * sizeof(uint32_t); } auto samsung_table_offset_size = samsung_mode ? num_files * sizeof(uint32_t) : 0; return total_file_location_size + total_class_data_size + num_files * sizeof(uint32_t) + // location len num_files * sizeof(uint32_t) + // location checksum num_files * sizeof(uint32_t) + // file offset samsung_table_offset_size; } static std::vector<DexFileListing_064::DexFile_064> build( const std::vector<DexInput>& dex_input, uint32_t& next_offset, bool samsung_mode); static void write( FileHandle& fh, const std::vector<DexFileListing_064::DexFile_064>& dex_files, bool samsung_mode) { for (const auto& file : dex_files) { uint32_t location_len = file.location.size(); write_word(fh, location_len); ConstBuffer location{file.location.c_str(), location_len}; // Locations are *not* null terminated. write_buf(fh, location); write_word(fh, file.location_checksum); write_word(fh, file.file_offset); if (samsung_mode) { write_word(fh, file.lookup_table_offset); } write_vec(fh, file.class_offsets); } } bool is_samsung() const { return is_samsung_; } private: std::vector<DexFile_064> dex_files_; bool is_samsung_ = false; }; // Collection of all the headers of all the dex files found in the oat. class DexFiles { public: MOVABLE(DexFiles); // buf should start at the beginning of the OAT file, as the offsets // in DexFileListing are relative to the beginning of the OAT file. DexFiles(const DexFileListing& dex_file_listing, ConstBuffer buf) { for (const auto& file_offset : dex_file_listing.dex_file_offsets()) { auto dex_header_buf = buf.slice(file_offset); auto dh = DexFileHeader::parse(dex_header_buf); headers_.push_back(dh); auto dex_buf = buf.slice(file_offset, file_offset + dh.file_size); dexes_.push_back(dex_buf); } } void print() { for (const auto& e : headers_) { printf( "DexFile: { \ file_size: 0x%08x(%u), \ num_classes: 0x%08x(%u)}\n", e.file_size, e.file_size, e.class_defs_size, e.class_defs_size); } size_t index = 0; for (const auto& e : dexes_) { print_dex_opcodes(reinterpret_cast<const uint8_t*>(e.ptr), headers_[index].file_size); index++; } } const std::vector<DexFileHeader>& headers() const { return headers_; } private: std::vector<DexFileHeader> headers_; std::vector<ConstBuffer> dexes_; }; class OatClasses_079 : public OatClasses { public: struct DexClasses { std::string dex_file; std::vector<ClassInfo> class_info; std::vector<std::string> class_names; }; OatClasses_079() = default; OatClasses_079(const DexFileListing_079& dex_file_listing, const DexFiles& dex_files, ConstBuffer oat_buf); MOVABLE(OatClasses_079); void print(); void print_unverified_classes(); template <typename DexFileType> static void write(const std::vector<DexFileType>& dex_files, FileHandle& cksum_fh); protected: std::vector<DexClasses> classes_; }; class OatClasses_124 : public OatClasses_079 { public: OatClasses_124() = default; OatClasses_124(const DexFileListing_079& dex_file_listing, const DexFiles& dex_files, ConstBuffer oat_buf, ConstBuffer dex_buf); }; OatClasses_124::OatClasses_124(const DexFileListing_079& dex_file_listing, const DexFiles& dex_files, ConstBuffer oat_buf, ConstBuffer dex_buf) { foreach_pair(dex_file_listing.dex_files(), dex_files.headers(), [&](const DexFileListing_079::DexFile_079& listing, const DexFileHeader& header) { auto classes_offset = listing.classes_offset; DexClasses dex_classes; dex_classes.dex_file = listing.location; DexIdBufs id_bufs(dex_buf, listing.file_offset, header); // classes_offset points to an array of pointers (offsets) to // ClassInfo for (unsigned int i = 0; i < header.class_defs_size; i++) { ClassInfo info; uint32_t info_offset; cur_ma()->memcpyAndMark( &info_offset, oat_buf.slice(classes_offset + i * sizeof(uint32_t)).ptr, sizeof(uint32_t)); cur_ma()->memcpyAndMark(&info, oat_buf.slice(info_offset).ptr, sizeof(ClassInfo)); // TODO: Handle compiled classes. Need to read method bitmap // size, and method bitmap. dex_classes.class_info.push_back(info); dex_classes.class_names.push_back(id_bufs.get_class_name(i)); } classes_.push_back(dex_classes); }); } class OatClasses_064 : public OatClasses { public: static void write( const std::vector<DexFileListing_064::DexFile_064>& dex_files, FileHandle& cksum_fh) { // offsets were already written to the DexFileListing_064. for (const auto& file : dex_files) { if (file.class_offsets.empty()) { continue; } CHECK(file.class_offsets[0] == cksum_fh.bytes_written()); for (const auto& info : file.class_info) { write_obj(cksum_fh, info); } } } }; OatClasses_079::OatClasses_079(const DexFileListing_079& dex_file_listing, const DexFiles& dex_files, ConstBuffer oat_buf) { foreach_pair( dex_file_listing.dex_files(), dex_files.headers(), [&](const DexFileListing_079::DexFile_079& listing, const DexFileHeader& header) { auto classes_offset = listing.classes_offset; DexClasses dex_classes; dex_classes.dex_file = listing.location; DexIdBufs id_bufs(oat_buf, listing.file_offset, header); // classes_offset points to an array of pointers (offsets) to // ClassInfo for (unsigned int i = 0; i < header.class_defs_size; i++) { ClassInfo info; uint32_t info_offset; cur_ma()->memcpyAndMark( &info_offset, oat_buf.slice(classes_offset + i * sizeof(uint32_t)).ptr, sizeof(uint32_t)); cur_ma()->memcpyAndMark( &info, oat_buf.slice(info_offset).ptr, sizeof(ClassInfo)); // TODO: Handle compiled classes. Need to read method bitmap size, // and method bitmap. CHECK(info.type == static_cast<uint16_t>(Type::kOatClassNoneCompiled), "Parsing for compiled classes not implemented"); dex_classes.class_info.push_back(info); dex_classes.class_names.push_back(id_bufs.get_class_name(i)); } classes_.push_back(dex_classes); }); } void OatClasses_079::print() { for (const auto& e : classes_) { printf(" { Classes for dex %s\n", e.dex_file.c_str()); int count = 0; for (const auto& info : e.class_info) { if (count == 0) { printf(" "); } printf("%s%s ", shortStatusStr(static_cast<Status>(info.status)), shortTypeStr(static_cast<Type>(info.type))); count++; if (count >= 32) { printf("\n"); count = 0; } } printf(" }\n"); } } void OatClasses_079::print_unverified_classes() { printf("unverified classes:\n"); for (const auto& e : classes_) { printf(" %s\n", e.dex_file.c_str()); foreach_pair(e.class_info, e.class_names, [&](const ClassInfo& info, const std::string& name) { if (info.status < static_cast<int>(Status::kStatusVerified)) { printf(" %s unverified (status: %s)\n", name.c_str(), statusStr(info.status)); } }); } } template <typename DexFileType> void OatClasses_079::write(const std::vector<DexFileType>& dex_files, FileHandle& cksum_fh) { #ifdef DEBUG_LOG printf("WRITING OatClasses:\n"); #endif size_t dex_count = 0; for (const auto& dex_file : dex_files) { CHECK(dex_file.classes_offset == cksum_fh.bytes_written()); const auto num_classes = dex_file.num_classes; uint32_t table_offset = dex_file.classes_offset + num_classes * sizeof(uint32_t); #ifdef DEBUG_LOG printf( "WRITING OatClasses for dex[%zu]: \ #classes: %u :: \ #offset: %u (-> %u)\n", dex_count, num_classes, dex_file.classes_offset, table_offset); #endif // write pointers to ClassInfo. for (size_t i = 0; i < num_classes; i++) { write_word(cksum_fh, table_offset + i * sizeof(uint32_t)); #ifdef DEBUG_LOG printf("#ClassOffsets[%zu] -> %zu\n", i, table_offset + i * sizeof(uint32_t)); #endif } CHECK(table_offset == cksum_fh.bytes_written()); // Write ClassInfo structs. OatClasses::ClassInfo info(OatClasses::Status::kStatusVerified, OatClasses::Type::kOatClassNoneCompiled); for (size_t i = 0; i < num_classes; i++) { write_obj(cksum_fh, info); #ifdef DEBUG_LOG printf("#OatClass[%zu]:%u :: type: %u\n", i, table_offset, info.type); #endif table_offset += sizeof(OatClasses::ClassInfo); } CHECK(table_offset == cksum_fh.bytes_written()); dex_count++; } } class SamsungLookupTablesNil { public: template <typename DexFileListingType> static void write(const std::vector<DexInput>&, const DexFileListingType&, FileHandle&) {} }; // Code to generate the lookup tables used on Samsung 5.0 phones. // // This is very similar to the LookupTables class, however, almost all the // details are slightly different (e.g., same hash function, but samsung starts // the hash at 1, instead of 0). As such there's not any value in trying to // factor any common code out here, it would just result in a huge mess. class SamsungLookupTables { public: MOVABLE(SamsungLookupTables); struct LookupTableEntry { uint32_t hash; uint32_t str_offset; uint32_t type_index; }; struct LookupTable { LookupTable(LookupTable&&) = default; LookupTable& operator=(LookupTable&&) = default; std::unique_ptr<LookupTableEntry[]> data; uint32_t size; size_t byte_size() const { return size * sizeof(LookupTableEntry); } }; static bool supportedSize(uint32_t num_class_defs) { return num_class_defs != 0u && num_class_defs <= std::numeric_limits<uint16_t>::max(); } static uint32_t numEntries(uint32_t num_classes) { return supportedSize(num_classes) ? roundUpToPowerOfTwo(num_classes) : 0u; } static uint32_t rawSize(uint32_t num_classes) { return numEntries(num_classes) * sizeof(LookupTableEntry); } static void write( const std::vector<DexInput>& dex_input_vec, const std::vector<DexFileListing_064::DexFile_064>& dex_files, FileHandle& cksum_fh) { foreach_pair( dex_input_vec, dex_files, [&](const DexInput& dex_input, const DexFileListing_064::DexFile_064& dex_file) { CHECK(dex_file.lookup_table_offset == cksum_fh.bytes_written()); auto table = build_lookup_table(dex_input.filename); auto buf = ConstBuffer{reinterpret_cast<const char*>(table.data.get()), table.byte_size()}; write_buf(cksum_fh, buf); }); } private: static void insert(LookupTableEntry* table, uint32_t lookup_table_size, uint32_t hash, uint32_t string_offset, uint16_t value) { const auto mask = lookup_table_size - 1; const auto start_bucket = hash & mask; auto bucket = start_bucket; do { auto& entry = table[bucket]; if (entry.str_offset == 0) { entry.hash = hash; entry.str_offset = string_offset; entry.type_index = value; return; } bucket = (bucket + 1) & mask; } while (bucket != start_bucket); // since the size of the table is chosen to be larger than the number of // items to insert, this should never happen. fprintf(stderr, "Error: ran out of hash table space"); } static uint32_t hash_str(const std::string& str) { uint32_t hash = 1; const char* chars = str.c_str(); while (*chars != '\0') { hash = hash * 31 + *chars; chars++; } return hash; } static LookupTable build_lookup_table(const std::string& filename) { auto dex_fh = FileHandle(fopen(filename.c_str(), "r")); DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); const auto num_type_ids = header.type_ids_size; const auto lookup_table_size = numEntries(num_type_ids); std::unique_ptr<LookupTableEntry[]> table_buf( new LookupTableEntry[lookup_table_size]); memset(table_buf.get(), 0, lookup_table_size * sizeof(LookupTableEntry)); // Read type ids array. std::unique_ptr<uint32_t[]> typeid_buf(new uint32_t[num_type_ids]); CHECK(dex_fh.seek_set(header.type_ids_off)); CHECK(dex_fh.fread(typeid_buf.get(), sizeof(uint32_t), num_type_ids) == num_type_ids); // Read the string ids array. const auto num_string_ids = header.string_ids_size; std::unique_ptr<uint32_t[]> stringid_buf(new uint32_t[num_string_ids]); CHECK(dex_fh.seek_set(header.string_ids_off)); CHECK(dex_fh.fread(stringid_buf.get(), sizeof(uint32_t), num_string_ids) == num_string_ids); constexpr int kTypeNameBufSize = 256; char type_name_buf[kTypeNameBufSize] = {}; struct Retry { uint32_t string_offset; uint16_t data; uint32_t hash; }; std::vector<Retry> retry_indices; for (unsigned int i = 0; i < num_type_ids; i++) { const auto string_id = typeid_buf[i]; CHECK(string_id < num_string_ids); const auto string_offset = stringid_buf[string_id]; dex_fh.seek_set(string_offset); auto read_size = dex_fh.fread(type_name_buf, sizeof(char), kTypeNameBufSize); CHECK(read_size > 0); auto ptr = type_name_buf; const auto str_size = read_uleb128(&ptr) + 1; const auto str_start = ptr - type_name_buf; std::string type_name; if (str_start + str_size >= kTypeNameBufSize) { std::unique_ptr<char[]> large_class_name_buf(new char[str_size]); dex_fh.seek_set(string_offset + str_start); CHECK(dex_fh.fread(large_class_name_buf.get(), sizeof(char), str_size) == str_size); type_name = std::string(large_class_name_buf.get(), str_size); } else { type_name = std::string(ptr, str_size); } const auto hash = hash_str(type_name); insert(table_buf.get(), lookup_table_size, hash, string_offset, i); } return LookupTable{std::move(table_buf), lookup_table_size}; } }; // Type lookup tables for all dex files in the oat file. // - The beginning offset of the lookup-table for the dex is specified in // the DexFileListing. // - The number of entries in the table is equal to the first power of 2 // which is larger than the number of classes in the dex file // (DexFileHeader::class_defs_size). // - Each entry in the table is a LookupTableEntry struct. class LookupTables { public: MOVABLE(LookupTables); // LookupTableEntry is exactly the layout of the entry in the file. struct LookupTableEntry { uint32_t str_offset; // the offset, relative to the beginning of the // dexfile, where the name of the class begins. uint16_t data; uint16_t next_pos_delta; }; // A mostly-materialized view for a single table - entries points // directly into the buffer. struct LookupTable { uint32_t dex_file_offset; std::string dex_file; const LookupTableEntry* entries; uint32_t num_entries; }; LookupTables() = default; LookupTables(const DexFileListing_079& dex_file_listing, const DexFiles& dex_files, ConstBuffer oat_buf) : oat_buf_(oat_buf) { CHECK(dex_file_listing.dex_files().size() == dex_files.headers().size()); auto listing_it = dex_file_listing.dex_files().begin(); auto file_it = dex_files.headers().begin(); tables_.reserve(dex_file_listing.dex_files().size()); for (unsigned int i = 0; i < dex_file_listing.dex_files().size(); i++) { auto table_offset = listing_it->lookup_table_offset; const auto& header = *file_it; auto num_entries = numEntries(header.class_defs_size); auto ptr = oat_buf.slice(table_offset).ptr; cur_ma()->markRangeConsumed(ptr, num_entries * sizeof(LookupTableEntry)); tables_.push_back(LookupTable{ listing_it->file_offset, listing_it->location, reinterpret_cast<const LookupTableEntry*>(ptr), num_entries}); ++listing_it; ++file_it; } } void print() { for (const auto& e : tables_) { printf( "Type_lookup_table[%s]: { \ num_entries: %u, \ entries: [", e.dex_file.c_str(), e.num_entries); for (unsigned int i = 0; i < e.num_entries; i++) { const auto& entry = e.entries[i]; if (entry.str_offset != 0) { printf( "{str: %s, \ str offset: 0x%08x}", oat_buf_.slice(e.dex_file_offset + entry.str_offset).ptr, entry.str_offset); } } printf("]}\n"); } } static uint32_t numEntries(uint32_t num_classes) { return supportedSize(num_classes) ? roundUpToPowerOfTwo(num_classes) : 0u; } template <typename DexFileType> static void write(const std::vector<DexInput>& dex_input_vec, const std::vector<DexFileType>& dex_files, FileHandle& cksum_fh) { foreach_pair( dex_input_vec, dex_files, [&](const DexInput& dex_input, const DexFileListing_079::DexFile_079& dex_file) { CHECK(dex_file.lookup_table_offset == cksum_fh.bytes_written()); const auto num_classes = dex_file.num_classes; const auto lookup_table_size = numEntries(num_classes); const auto lookup_table_byte_size = lookup_table_size * sizeof(LookupTableEntry); auto lookup_table_buf = build_lookup_table(dex_input.filename, lookup_table_size); auto buf = ConstBuffer{reinterpret_cast<const char*>(lookup_table_buf.get()), lookup_table_byte_size}; write_buf(cksum_fh, buf); }); } private: static uint32_t hash_str(const std::string& str) { uint32_t hash = 0; const char* chars = str.c_str(); while (*chars != '\0') { hash = hash * 31 + *chars; chars++; } return hash; } static uint16_t make_lt_data(uint16_t class_def_idx, uint32_t hash, uint32_t mask) { uint16_t hash_mask = static_cast<uint16_t>(~mask); return (static_cast<uint16_t>(hash) & hash_mask) | class_def_idx; } static bool insert_no_probe(LookupTableEntry* table, const LookupTableEntry& entry, uint32_t hash, uint32_t mask) { const uint32_t pos = hash & mask; if (table[pos].str_offset != 0) { return false; } table[pos] = entry; table[pos].next_pos_delta = 0; return true; } static void insert(LookupTableEntry* table, const LookupTableEntry& entry, uint32_t hash, uint32_t mask) { // find the last entry in this chain. uint32_t pos = hash & mask; while (table[pos].next_pos_delta != 0) { pos = (pos + table[pos].next_pos_delta) & mask; } // find the next empty entry uint32_t delta = 1; while (table[(pos + delta) & mask].str_offset != 0) { delta++; } uint32_t next_pos = (pos + delta) & mask; table[pos].next_pos_delta = delta; table[next_pos] = entry; table[next_pos].next_pos_delta = 0; } static std::unique_ptr<LookupTableEntry[]> build_lookup_table( const std::string& filename, uint32_t lookup_table_size) { std::unique_ptr<LookupTableEntry[]> table_buf( new LookupTableEntry[lookup_table_size]); memset(table_buf.get(), 0, lookup_table_size * sizeof(LookupTableEntry)); auto dex_fh = FileHandle(fopen(filename.c_str(), "r")); DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); const auto num_classes = header.class_defs_size; const auto mask = lookup_table_size - 1; // TODO: This is probably the most memory hungry part of the whole building // process, but total usage should still be <1MB for all the class strings. // if this proves to be a problem we can build the lookup table with redex // and ship it to the phone. // Read type ids array. const auto num_type_ids = header.type_ids_size; std::unique_ptr<uint32_t[]> typeid_buf(new uint32_t[num_type_ids]); CHECK(dex_fh.seek_set(header.type_ids_off)); CHECK(dex_fh.fread(typeid_buf.get(), sizeof(uint32_t), num_type_ids) == num_type_ids); // Read the string ids array. const auto num_string_ids = header.string_ids_size; std::unique_ptr<uint32_t[]> stringid_buf(new uint32_t[num_string_ids]); CHECK(dex_fh.seek_set(header.string_ids_off)); CHECK(dex_fh.fread(stringid_buf.get(), sizeof(uint32_t), num_string_ids) == num_string_ids); CHECK(dex_fh.seek_set(header.class_defs_off)); std::unique_ptr<DexClassDef[]> class_defs_buf(new DexClassDef[num_classes]); CHECK(dex_fh.fread(class_defs_buf.get(), sizeof(DexClassDef), num_classes) == num_classes); constexpr int kClassNameBufSize = 256; char class_name_buf[kClassNameBufSize] = {}; struct Retry { uint32_t string_offset; uint16_t data; uint32_t hash; }; std::vector<Retry> retry_indices; for (unsigned int i = 0; i < num_classes; i++) { const auto class_idx = class_defs_buf[i].class_idx; CHECK(class_idx < num_type_ids); const auto string_id = typeid_buf[class_idx]; CHECK(string_id < num_string_ids); const auto string_offset = stringid_buf[string_id]; dex_fh.seek_set(string_offset); auto read_size = dex_fh.fread(class_name_buf, sizeof(char), kClassNameBufSize); CHECK(read_size > 0); auto ptr = class_name_buf; const auto str_size = read_uleb128(&ptr) + 1; const auto str_start = ptr - class_name_buf; std::string class_name; if (str_start + str_size >= kClassNameBufSize) { std::unique_ptr<char[]> large_class_name_buf(new char[str_size]); dex_fh.seek_set(string_offset + str_start); CHECK(dex_fh.fread(large_class_name_buf.get(), sizeof(char), str_size) == str_size); class_name = std::string(large_class_name_buf.get(), str_size); } else { class_name = std::string(ptr, str_size); } const auto hash = hash_str(class_name); const auto data = make_lt_data(i, hash, mask); if (!insert_no_probe(table_buf.get(), LookupTableEntry{string_offset, data, 0}, hash, mask)) { retry_indices.emplace_back(Retry{string_offset, data, hash}); } } for (const auto& retry : retry_indices) { insert(table_buf.get(), LookupTableEntry{retry.string_offset, retry.data, 0}, retry.hash, mask); } return table_buf; } static bool supportedSize(uint32_t num_class_defs) { return num_class_defs != 0u && num_class_defs <= std::numeric_limits<uint16_t>::max(); } ConstBuffer oat_buf_; std::vector<LookupTable> tables_; }; class LookupTables_Nil : public LookupTables { public: template <typename DexFileListingType> static void write(const std::vector<DexInput>&, const DexFileListingType&, FileHandle&) {} }; // Handles version 064 and 045. class OatFile_064 : public OatFile { public: UNCOPYABLE(OatFile_064); MOVABLE(OatFile_064); static std::unique_ptr<OatFile> parse(bool dex_files_only, ConstBuffer buf, size_t oat_offset) { auto header = OatHeader::parse(buf); auto key_value_store = KeyValueStore( buf.slice(header.size()).truncate(header.key_value_store_size)); auto rest = buf.slice(header.size() + header.key_value_store_size); DexFileListing_064 dfl(dex_files_only, static_cast<OatVersion>(header.common.version), header.dex_file_count, rest, buf); DexFiles dex_files(dfl, buf); return std::unique_ptr<OatFile>(new OatFile_064(header, key_value_store, std::move(dfl), std::move(dex_files), oat_offset)); } void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Header:\n"); header_.print(); printf("Key/Value store:\n"); key_value_store_.print(); printf("Dex File Listing:\n"); dex_file_listing_.print(); printf("Dex Files:\n"); dex_files_.print(); if (dump_classes) { printf("Classes:\n"); dex_file_listing_.print_classes(); } if (print_unverified_classes) { dex_file_listing_.print_unverified_classes(); } } Status status() override { return Status::PARSE_SUCCESS; } std::vector<OatDexFile> get_oat_dexfiles() override { std::vector<OatDexFile> ret; ret.reserve(dex_file_listing_.dex_files().size()); foreach_pair(dex_file_listing_.dex_files(), dex_files_.headers(), [&](const DexFileListing_064::DexFile_064& dex_file, const DexFileHeader& header) { ret.emplace_back(dex_file.location, dex_file.file_offset, header.file_size); }); return ret; } std::unique_ptr<std::string> get_art_image_loc() const override { auto img_loc = key_value_store_.get("image-location"); if (img_loc == nullptr) { return nullptr; } return std::unique_ptr<std::string>(new std::string(img_loc)); } bool created_by_oatmeal() const override { return key_value_store_.has_key(kCreatedByOatmeal); } std::string version_string() const override { char buf[5] = {}; memcpy(buf, &header_.common.version, 4); return std::string(buf); } size_t oat_offset() const override { return oat_offset_; } bool is_samsung() const override { return dex_file_listing_.is_samsung(); } static Status build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data); private: OatFile_064(OatHeader h, KeyValueStore kv, DexFileListing_064 dfl, DexFiles dex_files, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_file_listing_(std::move(dfl)), dex_files_(std::move(dex_files)), oat_offset_(oat_data_offset) {} OatHeader header_; KeyValueStore key_value_store_; DexFileListing_064 dex_file_listing_; DexFiles dex_files_; size_t oat_offset_; }; // OatFile format for 079 and 088. (088 may have changes that don't // show up with verify-none. So far it appears to be identical.) class OatFile_079 : public OatFile { public: UNCOPYABLE(OatFile_079); MOVABLE(OatFile_079); static std::unique_ptr<OatFile> parse(bool dex_files_only, ConstBuffer buf, size_t oat_offset) { auto header = OatHeader::parse(buf); auto key_value_store = KeyValueStore( buf.slice(header.size()).truncate(header.key_value_store_size)); auto rest = buf.slice(header.size() + header.key_value_store_size); DexFileListing_079 dfl(header.dex_file_count, rest); DexFiles dex_files(dfl, buf); if (dex_files_only) { return std::unique_ptr<OatFile>(new OatFile_079(header, key_value_store, std::move(dfl), std::move(dex_files), oat_offset)); } LookupTables lookup_tables(dfl, dex_files, buf); OatClasses_079 oat_classes(dfl, dex_files, buf); return std::unique_ptr<OatFile>(new OatFile_079(header, key_value_store, std::move(dfl), std::move(dex_files), std::move(lookup_tables), std::move(oat_classes), oat_offset)); } void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Header:\n"); header_.print(); printf("Key/Value store:\n"); key_value_store_.print(); printf("Dex File Listing:\n"); dex_file_listing_.print(); printf("Dex Files:\n"); dex_files_.print(); if (dump_tables) { printf("LookupTables:\n"); lookup_tables_.print(); } if (dump_classes) { printf("Classes:\n"); oat_classes_.print(); } if (print_unverified_classes) { oat_classes_.print_unverified_classes(); } } Status status() override { return Status::PARSE_SUCCESS; } static Status build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data); std::vector<OatDexFile> get_oat_dexfiles() override { std::vector<OatDexFile> ret; ret.reserve(dex_file_listing_.dex_files().size()); for (const auto& dex : dex_file_listing_.dex_files()) { ret.emplace_back(dex.location, dex.location_checksum, dex.file_offset); } return ret; } size_t oat_offset() const override { return oat_offset_; } // Samsung has no custom modifications (that i know of) on 079 and up, so // there's nothing to detect. bool is_samsung() const override { return false; } std::unique_ptr<std::string> get_art_image_loc() const override { auto img_loc = key_value_store_.get("image-location"); if (img_loc == nullptr) { return nullptr; } return std::unique_ptr<std::string>(new std::string(img_loc)); } bool created_by_oatmeal() const override { return key_value_store_.has_key(kCreatedByOatmeal); } std::string version_string() const override { char buf[5] = {}; memcpy(buf, &header_.common.version, 4); return std::string(buf); } private: OatFile_079(OatHeader h, KeyValueStore kv, DexFileListing_079 dfl, DexFiles dex_files, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_file_listing_(std::move(dfl)), dex_files_(std::move(dex_files)), oat_offset_(oat_data_offset) {} OatFile_079(OatHeader h, KeyValueStore kv, DexFileListing_079 dfl, DexFiles dex_files, LookupTables lt, OatClasses_079 oat_classes, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_file_listing_(std::move(dfl)), dex_files_(std::move(dex_files)), lookup_tables_(std::move(lt)), oat_classes_(std::move(oat_classes)), oat_offset_(oat_data_offset) {} OatHeader header_; KeyValueStore key_value_store_; DexFileListing_079 dex_file_listing_; DexFiles dex_files_; LookupTables lookup_tables_; OatClasses_079 oat_classes_; size_t oat_offset_; }; // OatFile format for 124/V131 // Key difference is the parsing of OAT and DEX has to be done in different // files instead on a single everything.oat file. class OatFile_124 : public OatFile { public: UNCOPYABLE(OatFile_124); MOVABLE(OatFile_124); template <typename DexFileListingType, typename OatFileType> static std::unique_ptr<OatFile> oatfile_124_131_parse( bool dex_files_only, ConstBuffer buf, size_t oat_offset, const std::vector<DexInput>& dexes) { if (dexes.size() != 1) { fprintf(stderr, "V124/V131 odex files must come accompained with one and only " "one vdex file\n"); return nullptr; } auto header = OatHeader::parse(buf); auto key_value_store = KeyValueStore( buf.slice(header.size()).truncate(header.key_value_store_size)); auto rest = buf.slice(header.size() + header.key_value_store_size); DexFileListingType dfl(header.dex_file_count, rest); auto dex_file_name = dexes[0].filename; auto dex_file = FileHandle(fopen(dex_file_name.c_str(), "r")); if (dex_file.get() == nullptr) { fprintf(stderr, "failed to open dex file %s %s\n", dex_file_name.c_str(), std::strerror(errno)); return nullptr; } auto dex_file_size = get_filesize(dex_file); // We don't run dumping during install on device, so it is allowed to // consume lots of memory. auto dex_file_contents = std::make_unique<char[]>(dex_file_size); auto dexFileBytesRead = fread(dex_file_contents.get(), 1, dex_file_size, dex_file.get()); if (dexFileBytesRead != dex_file_size) { fprintf(stderr, "Failed to read dex file %s (%zd)\n", std::strerror(errno), dexFileBytesRead); return nullptr; } ConstBuffer dex_file_buf{dex_file_contents.get(), dex_file_size}; cur_ma()->addBuffer(dex_file_buf); DexFiles dex_files(dfl, dex_file_buf); if (dex_files_only) { return std::unique_ptr<OatFile>(new OatFileType(header, key_value_store, std::move(dfl), std::move(dex_files), oat_offset)); } LookupTables lookup_tables(dfl, dex_files, buf); OatClasses_124 oat_classes(dfl, dex_files, buf, dex_file_buf); return std::unique_ptr<OatFile>(new OatFileType(header, key_value_store, std::move(dfl), std::move(dex_files), std::move(lookup_tables), std::move(oat_classes), oat_offset)); } static std::unique_ptr<OatFile> parse(bool dex_files_only, ConstBuffer buf, size_t oat_offset, const std::vector<DexInput>& dexes) { return oatfile_124_131_parse<DexFileListing_124, OatFile_124>( dex_files_only, buf, oat_offset, dexes); } void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Header:\n"); header_.print(); printf("Key/Value store:\n"); key_value_store_.print(); printf("Dex File Listing:\n"); dex_file_listing_->print(); printf("Dex Files:\n"); dex_files_.print(); if (dump_tables) { printf("LookupTables:\n"); lookup_tables_.print(); } if (dump_classes) { printf("Classes:\n"); oat_classes_.print(); } if (print_unverified_classes) { oat_classes_.print_unverified_classes(); } } bool is_samsung() const override { return false; } Status status() override { return Status::PARSE_SUCCESS; } static Status build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data); std::vector<OatDexFile> get_oat_dexfiles() override { std::vector<OatDexFile> ret; ret.reserve(dex_file_listing_->dex_files().size()); for (const auto& dex : dex_file_listing_->dex_files()) { ret.emplace_back(dex.location, dex.location_checksum, dex.file_offset); } return ret; } size_t oat_offset() const override { return oat_offset_; } std::unique_ptr<std::string> get_art_image_loc() const override { auto img_loc = key_value_store_.get("image-location"); if (img_loc == nullptr) { return nullptr; } return std::unique_ptr<std::string>(new std::string(img_loc)); } bool created_by_oatmeal() const override { return key_value_store_.has_key(kCreatedByOatmeal); } std::string version_string() const override { char buf[5] = {}; memcpy(buf, &header_.common.version, 4); return std::string(buf); } private: OatFile_124(OatHeader h, KeyValueStore kv, DexFileListing_124 dfl, DexFiles dex_files, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_files_(std::move(dex_files)), oat_offset_(oat_data_offset), dex_file_listing_(&dfl) {} OatFile_124(OatHeader h, KeyValueStore kv, DexFileListing_124 dfl, DexFiles dex_files, LookupTables lt, OatClasses_124 oat_classes, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_files_(std::move(dex_files)), lookup_tables_(std::move(lt)), oat_classes_(std::move(oat_classes)), oat_offset_(oat_data_offset), dex_file_listing_(&dfl) {} protected: OatFile_124(OatHeader h, KeyValueStore kv, DexFiles dex_files, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_files_(std::move(dex_files)), oat_offset_(oat_data_offset), dex_file_listing_(nullptr) {} OatFile_124(OatHeader h, KeyValueStore kv, DexFiles dex_files, LookupTables lt, OatClasses_124 oat_classes, size_t oat_data_offset) : header_(h), key_value_store_(std::move(kv)), dex_files_(std::move(dex_files)), lookup_tables_(std::move(lt)), oat_classes_(std::move(oat_classes)), oat_offset_(oat_data_offset), dex_file_listing_(nullptr) {} OatHeader header_; KeyValueStore key_value_store_; DexFiles dex_files_; LookupTables lookup_tables_; OatClasses_124 oat_classes_; size_t oat_offset_; private: std::unique_ptr<DexFileListing_124> dex_file_listing_; }; class OatFile_131 : public OatFile_124 { public: UNCOPYABLE(OatFile_131); MOVABLE(OatFile_131); void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Header:\n"); header_.print(); printf("Key/Value store:\n"); key_value_store_.print(); printf("Dex File Listing:\n"); dex_file_listing_.print(); printf("Dex Files:\n"); dex_files_.print(); if (dump_tables) { printf("LookupTables:\n"); lookup_tables_.print(); } if (dump_classes) { printf("Classes:\n"); oat_classes_.print(); } if (print_unverified_classes) { oat_classes_.print_unverified_classes(); } } bool is_samsung() const override { return false; } Status status() override { return Status::PARSE_SUCCESS; } static Status build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data); std::vector<OatDexFile> get_oat_dexfiles() override { std::vector<OatDexFile> ret; ret.reserve(dex_file_listing_.dex_files().size()); for (const auto& dex : dex_file_listing_.dex_files()) { ret.emplace_back(dex.location, dex.location_checksum, dex.file_offset); } return ret; } size_t oat_offset() const override { return oat_offset_; } std::unique_ptr<std::string> get_art_image_loc() const override { auto img_loc = key_value_store_.get("image-location"); if (img_loc == nullptr) { return nullptr; } return std::unique_ptr<std::string>(new std::string(img_loc)); } bool created_by_oatmeal() const override { return key_value_store_.has_key(kCreatedByOatmeal); } std::string version_string() const override { char buf[5] = {}; memcpy(buf, &header_.common.version, 4); return std::string(buf); } private: OatFile_131(OatHeader h, KeyValueStore kv, DexFileListing_131 dfl, DexFiles dex_files, size_t oat_data_offset) : OatFile_124(h, std::move(kv), std::move(dex_files), oat_data_offset), dex_file_listing_(std::move(dfl)) {} OatFile_131(OatHeader h, KeyValueStore kv, DexFileListing_131 dfl, DexFiles dex_files, LookupTables lt, OatClasses_124 oat_classes, size_t oat_data_offset) : OatFile_124(h, std::move(kv), std::move(dex_files), std::move(lt), std::move(oat_classes), oat_data_offset), dex_file_listing_(std::move(dfl)) {} DexFileListing_131 dex_file_listing_; }; class OatFile_Unknown : public OatFile { public: void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Unknown OAT file version!\n"); header_.print(); } Status status() override { return Status::PARSE_UNKNOWN_VERSION; } size_t oat_offset() const override { return 0; } static std::unique_ptr<OatFile> parse(ConstBuffer buf) { return std::unique_ptr<OatFile>(new OatFile_Unknown(buf)); } std::vector<OatDexFile> get_oat_dexfiles() override { return std::vector<OatDexFile>(); } std::unique_ptr<std::string> get_art_image_loc() const override { return nullptr; } bool created_by_oatmeal() const override { return false; } std::string version_string() const override { return ""; } bool is_samsung() const override { return false; } private: explicit OatFile_Unknown(ConstBuffer buf) { header_ = OatHeader_Common::parse(buf); } OatHeader_Common header_; }; class OatFile_Bad : public OatFile { public: void print(bool dump_classes, bool dump_tables, bool print_unverified_classes) override { printf("Bad magic number:\n"); header_.print(); } Status status() override { return Status::PARSE_BAD_MAGIC_NUMBER; } size_t oat_offset() const override { return 0; } static std::unique_ptr<OatFile> parse(ConstBuffer buf) { return std::unique_ptr<OatFile>(new OatFile_Bad(buf)); } std::vector<OatDexFile> get_oat_dexfiles() override { return std::vector<OatDexFile>(); } std::unique_ptr<std::string> get_art_image_loc() const override { return nullptr; } bool created_by_oatmeal() const override { return false; } std::string version_string() const override { return ""; } bool is_samsung() const override { return false; } private: explicit OatFile_Bad(ConstBuffer buf) { header_ = OatHeader_Common::parse(buf); } OatHeader_Common header_; }; } // namespace OatFile::~OatFile() = default; static std::unique_ptr<OatFile> parse_oatfile_impl( bool dex_files_only, ConstBuffer oatfile_buffer, const std::vector<DexInput>& dexes) { constexpr size_t kOatElfOffset = 0x1000; size_t oat_offset = 0; if (oatfile_buffer.len >= std::max(kOatElfOffset, sizeof(Elf32_Ehdr))) { Elf32_Ehdr header; memcpy(&header, oatfile_buffer.ptr, sizeof(Elf32_Ehdr)); if (header.e_ident[0] == 0x7f && header.e_ident[1] == 'E' && header.e_ident[2] == 'L' && header.e_ident[3] == 'F') { // .rodata starts at 0x1000 in every version of ART that i've seen. // If there are any where this isn't true, we'll have to actually read // out the offset of .rodata. oat_offset = 0x1000; oatfile_buffer = oatfile_buffer.slice(0x1000); } } if (oatfile_buffer.len < sizeof(OatHeader)) { return nullptr; } auto header = OatHeader_Common::parse(oatfile_buffer); // TODO: do we need to handle endian-ness? I think all platforms we // care about are little-endian. if (header.magic != kOatMagicNum) { return OatFile_Bad::parse(oatfile_buffer); } switch (static_cast<OatVersion>(header.version)) { case OatVersion::V_039: case OatVersion::V_045: case OatVersion::V_064: case OatVersion::V_067: return OatFile_064::parse(dex_files_only, oatfile_buffer, oat_offset); case OatVersion::V_079: case OatVersion::V_088: // 079 and 088 are the same as far as I can tell. return OatFile_079::parse(dex_files_only, oatfile_buffer, oat_offset); case OatVersion::V_124: case OatVersion::V_131: return OatFile_124::parse( dex_files_only, oatfile_buffer, oat_offset, dexes); case OatVersion::UNKNOWN: return OatFile_Unknown::parse(oatfile_buffer); } fprintf(stderr, "Unhandled oat version 0x%08x\n", header.version); return std::unique_ptr<OatFile>(nullptr); } std::unique_ptr<OatFile> OatFile::parse(ConstBuffer oatfile_buffer, const std::vector<DexInput>& dex_files, bool dex_files_only) { return parse_oatfile_impl(dex_files_only, oatfile_buffer, dex_files); } std::unique_ptr<OatFile> OatFile::parse_dex_files_only(ConstBuffer buf) { return parse_oatfile_impl(true, buf, std::vector<DexInput>()); } std::unique_ptr<OatFile> OatFile::parse_dex_files_only(void* ptr, size_t len) { return parse_dex_files_only(ConstBuffer{reinterpret_cast<char*>(ptr), len}); } ////////// building OatHeader build_header(OatVersion oat_version, const std::vector<DexInput>& dex_input, InstructionSet isa, uint32_t keyvalue_size, uint32_t oat_size, const ImageInfo_064* image_info) { OatHeader header; header.common.magic = kOatMagicNum; header.common.version = static_cast<uint32_t>(oat_version); // the checksum must be re-written after we've written // the rest of the file, as we don't know the checksum until then. header.common.adler32_checksum = 0xcdcdcdcd; header.instruction_set = isa; // This appears to be set to 1 on both x86 and arm, not clear if there is // ever a case where we need to parameterize this. header.instruction_set_features_bitmap = 1; header.dex_file_count = dex_input.size(); header.executable_offset = oat_size; header.key_value_store_size = keyvalue_size; if (image_info != nullptr) { header.image_patch_delta = image_info->patch_delta; header.image_file_location_oat_checksum = image_info->oat_checksum; header.image_file_location_oat_data_begin = image_info->data_begin; } // omitted fields default to zero, and should remain zero. return header; } std::vector<DexFileListing_064::DexFile_064> DexFileListing_064::build( const std::vector<DexInput>& dex_input, uint32_t& next_offset, bool samsung_mode) { // next_offset points to the first byte after the DexFileListing CHECK(is_aligned<4>(next_offset)); uint32_t total_lookup_table_size = 0; uint32_t total_dex_size = 0; uint32_t total_class_info_size = 0; std::vector<DexFileListing_064::DexFile_064> dex_files; dex_files.reserve(dex_input.size()); for (const auto& dex : dex_input) { auto dex_offset = next_offset + total_dex_size; auto dex_fh = FileHandle(fopen(dex.filename.c_str(), "r")); CHECK(dex_fh.get() != nullptr); auto file_size = get_filesize(dex_fh); // dex files are 4-byte aligned inside the oatfile. auto padded_size = align<4>(file_size); CHECK(file_size >= sizeof(DexFileHeader)); // read the header to get the count of classes. DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); const auto num_classes = header.class_defs_size; const auto num_types = header.type_ids_size; total_class_info_size += num_classes * sizeof(uint32_t); total_dex_size += padded_size; if (samsung_mode) { total_lookup_table_size += SamsungLookupTables::rawSize(num_types); } std::vector<ClassInfo> classes; for (unsigned int i = 0; i < num_classes; i++) { classes.push_back(ClassInfo(OatClasses::Status::kStatusVerified, OatClasses::Type::kOatClassNoneCompiled)); } dex_files.push_back(DexFileListing_064::DexFile_064( dex.location, header.checksum, dex_offset, // temporarily store a count, will be translated to an offset after this // loop. num_types, std::vector<uint32_t>(num_classes), classes)); } if (samsung_mode) { // need to adjust all dex offsets forward by the total lookup table size. for (auto& dex : dex_files) { dex.file_offset += total_lookup_table_size; } CHECK(is_aligned<4>(next_offset)); // adjust the lookup_table offsets for each dex. lookup_table_offset current // stores the number of types. for (auto& dex : dex_files) { const auto num_types = dex.lookup_table_offset; dex.lookup_table_offset = next_offset; const auto raw_size = SamsungLookupTables::rawSize(num_types); next_offset += raw_size; } } CHECK(is_aligned<4>(next_offset)); next_offset += total_dex_size; auto first_class_info_offset = next_offset; next_offset += total_class_info_size; // Adjust the class offset tables for each dex, now that we have accounted // for the dex size. for (auto& dex : dex_files) { for (auto& offset : dex.class_offsets) { offset = first_class_info_offset; first_class_info_offset += sizeof(ClassInfo); } } return dex_files; } std::vector<DexFileListing_079::DexFile_079> DexFileListing_079::build( const std::vector<DexInput>& dex_input, uint32_t& next_offset, bool /*samsung_mode*/) { uint32_t total_dex_size = 0; std::vector<DexFileListing_079::DexFile_079> dex_files; dex_files.reserve(dex_input.size()); for (const auto& dex : dex_input) { auto dex_offset = next_offset + total_dex_size; auto dex_fh = FileHandle(fopen(dex.filename.c_str(), "r")); CHECK(dex_fh.get() != nullptr); auto file_size = get_filesize(dex_fh); // dex files are 4-byte aligned inside the oatfile. auto padded_size = align<4>(file_size); total_dex_size += padded_size; CHECK(file_size >= sizeof(DexFileHeader)); // read the header to get the count of classes. DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); auto num_classes = header.class_defs_size; auto class_table_size = num_classes * sizeof(uint32_t) // array of pointers to ClassInfo structs + num_classes * sizeof(OatClasses::ClassInfo); auto lookup_table_size = LookupTables::numEntries(num_classes) * sizeof(LookupTables::LookupTableEntry); dex_files.push_back(DexFileListing_079::DexFile_079( dex.location, header.checksum, dex_offset, // temporarily store sizes instead of offsets here. // they will be replaced with offsets in the next loop. num_classes, class_table_size, lookup_table_size)); } next_offset += total_dex_size; CHECK(is_aligned<4>(next_offset)); // note non-const ref for (auto& dex_file : dex_files) { auto cur_size = dex_file.classes_offset; dex_file.classes_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } for (auto& dex_file : dex_files) { auto cur_size = dex_file.lookup_table_offset; dex_file.lookup_table_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } return dex_files; } std::vector<DexFileListing_124::DexFile_124> DexFileListing_124::build( const std::vector<DexInput>& dex_input, uint32_t& next_offset, bool /*samsung_mode*/) { CHECK(dex_input.size() == 1); std::vector<DexFileListing_124::DexFile_124> dex_files; dex_files.reserve(dex_input.size()); for (const auto& dex : dex_input) { // We load the dex bytecode in the VDEX file after the header and // the checksum for the DEX right after. auto dex_offset = sizeof(VdexFileHeader) + sizeof(uint32_t); auto dex_fh = FileHandle(fopen(dex.filename.c_str(), "r")); CHECK(dex_fh.get() != nullptr); auto file_size = get_filesize(dex_fh); CHECK(file_size >= sizeof(DexFileHeader)); // read the header to get the count of classes. DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); auto num_classes = header.class_defs_size; auto class_table_size = num_classes * sizeof(uint32_t) // array of pointers to ClassInfo structs + num_classes * sizeof(OatClasses::ClassInfo); auto lookup_table_size = LookupTables::numEntries(num_classes) * sizeof(LookupTables::LookupTableEntry); dex_files.push_back(DexFileListing_124::DexFile_124( dex.location, header.checksum, dex_offset, // temporarily store sizes instead of offsets here. // they will be replaced with offsets in the next loop. num_classes, class_table_size, lookup_table_size)); } CHECK(is_aligned<4>(next_offset)); // note non-const ref for (auto& dex_file : dex_files) { auto cur_size = dex_file.classes_offset; dex_file.classes_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } for (auto& dex_file : dex_files) { auto cur_size = dex_file.lookup_table_offset; dex_file.lookup_table_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } return dex_files; } std::vector<DexFileListing_131::DexFile_131> DexFileListing_131::build( const std::vector<DexInput>& dex_input, uint32_t& next_offset, bool /*samsung_mode*/) { CHECK(dex_input.size() == 1); std::vector<DexFileListing_131::DexFile_131> dex_files; dex_files.reserve(dex_input.size()); for (const auto& dex : dex_input) { // We load the dex bytecode in the VDEX file after the header and // the checksum for the DEX right after. auto dex_offset = sizeof(VdexFileHeader) + sizeof(uint32_t); auto dex_fh = FileHandle(fopen(dex.filename.c_str(), "r")); CHECK(dex_fh.get() != nullptr); auto file_size = get_filesize(dex_fh); CHECK(file_size >= sizeof(DexFileHeader)); // read the header to get the count of classes. DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); auto num_classes = header.class_defs_size; auto class_table_size = num_classes * sizeof(uint32_t) // array of pointers to ClassInfo structs + num_classes * sizeof(OatClasses::ClassInfo); auto lookup_table_size = LookupTables::numEntries(num_classes) * sizeof(LookupTables::LookupTableEntry); dex_files.push_back(DexFileListing_131::DexFile_131( dex.location, header.checksum, dex_offset, // temporarily store sizes instead of offsets here. // they will be replaced with offsets in the next loop. num_classes, class_table_size, lookup_table_size, 0, // dex_layout_sections_offset 0)); // method_bss_mapping_offset } CHECK(is_aligned<4>(next_offset)); // note non-const ref for (auto& dex_file : dex_files) { auto cur_size = dex_file.classes_offset; dex_file.classes_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } for (auto& dex_file : dex_files) { auto cur_size = dex_file.lookup_table_offset; dex_file.lookup_table_offset = next_offset; next_offset += cur_size; CHECK(is_aligned<4>(next_offset)); } return dex_files; } void write_dex_file(const DexInput& input, const QuickData* quick_data, FileHandle& cksum_fh) { if (quick_data != nullptr) { START_TRACE() quicken_dex(input.filename.c_str(), quick_data, cksum_fh); END_TRACE("quicken_dex") } else { START_TRACE() auto dex_fh = FileHandle(fopen(input.filename.c_str(), "r")); stream_file(dex_fh, cksum_fh); END_TRACE("stream_dex") } } template <typename DexFileListingType> void write_dex_files(const std::vector<DexInput>& dex_input, const std::vector<DexFileListingType>& dex_files, const QuickData* quick_data, FileHandle& cksum_fh) { foreach_pair(dex_input, dex_files, [&](const DexInput& input, const DexFileListingType& dex_file) { CHECK(dex_file.file_offset == cksum_fh.bytes_written()); write_dex_file(input, quick_data, cksum_fh); }); } // We only ship to 32 bit platforms so this is always 4. static constexpr size_t pointer_size = 4; static size_t types_size(size_t num_elements) { return std::max(num_elements * pointer_size, pointer_size); } static size_t methods_size(size_t num_elements) { return std::max(pointer_size * num_elements, pointer_size); } static size_t strings_size(size_t num_elements) { return pointer_size * num_elements; } static size_t fields_size(size_t num_elements) { return pointer_size * num_elements; } static size_t compute_bss_size_079(const std::vector<DexInput>& dex_files) { size_t ret = 0; for (const auto& e : dex_files) { auto dex_fh = FileHandle(fopen(e.filename.c_str(), "r")); DexFileHeader header = {}; CHECK(dex_fh.fread(&header, sizeof(DexFileHeader), 1) == 1); auto meth_offset = align<pointer_size>(types_size(header.type_ids_size)); auto strings_offset = align<pointer_size>(meth_offset + methods_size(header.method_ids_size)); auto fields_offset = align<pointer_size>( strings_offset + strings_size(header.string_ids_size)); auto size = align<pointer_size>(fields_offset + fields_size(header.field_ids_size)); ret += size; } return ret; } std::unique_ptr<ImageInfo_064> read_image_info_064( const std::string& art_image_location) { auto art_fh = FileHandle(fopen(art_image_location.c_str(), "r")); if (art_fh.get() == nullptr) { return std::unique_ptr<ImageInfo_064>(nullptr); } auto art_header = ArtImageHeader::parse(art_fh); if (!art_header) { return std::unique_ptr<ImageInfo_064>(nullptr); } return std::unique_ptr<ImageInfo_064>( new ImageInfo_064(art_header->patch_delta, art_header->oat_checksum, art_header->oat_data_begin)); } std::unique_ptr<QuickData> read_quick_data( const std::string& quick_data_location) { struct stat buffer; if (stat(quick_data_location.c_str(), &buffer) != 0) { fprintf(stderr, "Failed to locate quickening metadata file: %s\n", quick_data_location.c_str()); return std::unique_ptr<QuickData>(nullptr); } return std::make_unique<QuickData>(quick_data_location.c_str()); } template <typename DexFileListingType, typename OatClassesType, typename LookupTablesType, typename SamsungLookupTablesType> OatFile::Status build_oatfile(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { const std::vector<KeyValueStore::KeyValue> key_value = { {"classpath", ""}, {"compiler-filter", "verify-none"}, {"debuggable", "false"}, // What ever will happen if art tries to use this? {"dex2oat-cmdline", "--oat-file=/dev/null --dex-file=/dev/null"}, {"dex2oat-host", "X86"}, {"has-patch-info", "false"}, {"native-debuggable", "false"}, {"image-location", art_image_location.c_str()}, {"pic", "false"}, {kCreatedByOatmeal, "true"}}; ////////// Gather image info from boot.art and boot.oat std::unique_ptr<ImageInfo_064> image_info; if (oat_version == OatVersion::V_067 || oat_version == OatVersion::V_064 || oat_version == OatVersion::V_045 || oat_version == OatVersion::V_039) { image_info = read_image_info_064(art_image_location); } ////////// Compute sizes and offsets. const auto keyvalue_size = KeyValueStore::compute_size(key_value); const auto dex_file_listing_size = DexFileListingType::compute_size(dex_input, samsung_mode); // Neither the keyvalue store or the DexFileListing require alignment. uint32_t next_offset = align<4>(OatHeader::size(oat_version) + keyvalue_size + dex_file_listing_size); // next_offset points to end of last dexfile listing. auto dex_files = DexFileListingType::build(dex_input, next_offset, samsung_mode); auto oat_size = align<0x1000>(next_offset); auto header = build_header( oat_version, dex_input, isa, keyvalue_size, oat_size, image_info.get()); ////////// Write the file. auto oat_fh = FileHandle(fopen(oat_file_name.c_str(), "w")); if (oat_fh.get() == nullptr) { return OatFile::Status::BUILD_IO_ERROR; } if (write_elf) { write_padding(oat_fh, 0, 0x1000); oat_fh.set_seek_reference_to_fpos(); oat_fh.reset_bytes_written(); } header.write(oat_fh); // Write key value store. KeyValueStore::write(oat_fh, key_value); // write DexFileListing DexFileListingType::write(oat_fh, dex_files, samsung_mode); // Write padding to align to 4 bytes. auto padding = align<4>(oat_fh.bytes_written()) - oat_fh.bytes_written(); char buf[4] = {}; write_buf(oat_fh, ConstBuffer{buf, padding}); // Write lookup tables. if (samsung_mode) { SamsungLookupTablesType::write(dex_input, dex_files, oat_fh); } write_dex_files(dex_input, dex_files, quick_data, oat_fh); OatClassesType::write(dex_files, oat_fh); LookupTablesType::write(dex_input, dex_files, oat_fh); // Pad with 0s up to oat_size write_padding(oat_fh, 0, oat_size - oat_fh.bytes_written()); ////////// Update header with final checksum. CHECK(oat_fh.seek_begin()); // Note: So far, I can't replicate the checksum computation done by // dex2oat. It appears that the file is written in a fairly arbitrary // order, and the checksum is computed as those sections are written. // Fortunately, art does not seem to verify the checksum at any point. // We don't even attempt to compute the checksum now, as it takes a few // seconds to do so. header.common.adler32_checksum = 0xcdcdcdcd; write_obj(oat_fh, header.common); if (write_elf) { oat_fh.set_seek_reference(0); oat_fh.seek_begin(); ElfWriter section_headers(oat_version); section_headers.build(isa, oat_size, compute_bss_size_079(dex_input)); section_headers.write(oat_fh); } return OatFile::Status::BUILD_SUCCESS; } void write_vdex_header(FileHandle& fh, VdexVersion vdex_version, uint32_t num_dex_files, uint32_t dex_size, uint32_t verifier_deps_size, uint32_t quickening_info_size, uint32_t vdex_checksum) { write_word(fh, kVdexMagicNum); write_word(fh, static_cast<uint32_t>(vdex_version)); write_word(fh, num_dex_files); write_word(fh, dex_size); write_word(fh, verifier_deps_size); write_word(fh, quickening_info_size); write_word(fh, vdex_checksum); } template <typename DexFileListingType> OatFile::Status build_vdex_odex_pairs(const std::string& oat_file_name, OatVersion oat_version, const DexInput& dex_input, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { const std::vector<KeyValueStore::KeyValue> key_value = { {"classpath", ""}, {"compiler-filter", "assume-verified"}, // Oreo will reject any OAT file that doesn't set this flag. {"concurrent-copying", "true"}, {"debuggable", "false"}, // What ever will happen if art tries to use this? {"dex2oat-cmdline", "--oat-file=/dev/null --dex-file=/dev/null"}, {"dex2oat-host", "X86"}, {"has-patch-info", "false"}, {"native-debuggable", "false"}, {"image-location", art_image_location.c_str()}, {"pic", "false"}, {kCreatedByOatmeal, "true"}}; std::vector<DexInput> single_dex_input; single_dex_input.push_back(dex_input); ////////// Compute sizes and offsets. const auto keyvalue_size = KeyValueStore::compute_size(key_value); const auto dex_file_listing_size = DexFileListingType::compute_size(single_dex_input, samsung_mode); // Neither the keyvalue store or the DexFileListing require alignment. uint32_t oat_dex_files_offset = OatHeader::size(oat_version) + keyvalue_size; uint32_t next_offset = align<4>(OatHeader::size(oat_version) + keyvalue_size + dex_file_listing_size); auto dex_files = DexFileListingType::build(single_dex_input, next_offset, samsung_mode); auto oat_size = align<0x1000>(next_offset); auto header = build_header( oat_version, single_dex_input, isa, keyvalue_size, oat_size, nullptr); printf("Oat Size: %u\n", oat_size); ////////// Write the file. auto oat_fh = FileHandle(fopen(oat_file_name.c_str(), "w")); if (oat_fh.get() == nullptr) { return OatFile::Status::BUILD_IO_ERROR; } if (write_elf) { write_padding(oat_fh, 0, 0x1000); oat_fh.set_seek_reference_to_fpos(); oat_fh.reset_bytes_written(); } if (oat_version == OatVersion::V_131) { CHECK(oat_dex_files_offset != 0, "OatDexFiles offset can't be zero"); header.oat_dex_files_offset = oat_dex_files_offset; } header.write(oat_fh); // Write key value store. KeyValueStore::write(oat_fh, key_value); // write DexFileListing DexFileListingType::write(oat_fh, dex_files, samsung_mode); // Write padding to align to 4 bytes. auto padding = align<4>(oat_fh.bytes_written()) - oat_fh.bytes_written(); char buf[4] = {}; write_buf(oat_fh, ConstBuffer{buf, padding}); // Write lookup tables. CHECK(oat_file_name.substr(oat_file_name.size() - 5) == std::string(".odex"), "V124/V131 Oatmeal should generate .odex files"); auto vdex_file_name = oat_file_name.substr(0, oat_file_name.size() - 4) + std::string("vdex"); printf("VDEX output file: %s\n", vdex_file_name.c_str()); const auto& dex_input_filename = dex_input.filename; // This will open the DEX file twice, we need its size first to // write it in the VDEX header. uint32_t dex_file_size = 0; auto dex_fh = FileHandle(fopen(dex_input_filename.c_str(), "r")); dex_file_size = get_filesize(dex_fh); // Retrieve the DEX checksum to store it just after the VDEX header uint32_t dex_checksum = 0x0; dex_fh.seek_set(8); dex_fh.fread(&dex_checksum, sizeof(uint32_t), 1); dex_fh.seek_set(0); auto vdex_fh = FileHandle(fopen(vdex_file_name.c_str(), "w")); write_vdex_header( vdex_fh, vdexVersion(oat_version), 1, dex_file_size, 0, 0, dex_checksum); write_dex_file(dex_input, quick_data, vdex_fh); OatClasses_124::write(dex_files, oat_fh); LookupTables::write(single_dex_input, dex_files, oat_fh); ////////// Update header with final checksum. CHECK(oat_fh.seek_begin()); // Note: So far, I can't replicate the checksum computation done by // dex2oat. It appears that the file is written in a fairly arbitrary // order, and the checksum is computed as those sections are written. // Fortunately, art does not seem to verify the checksum at any point. // We don't even attempt to compute the checksum now, as it takes a few // seconds to do so. header.common.adler32_checksum = 0xcdcdcdcd; write_obj(oat_fh, header.common); if (write_elf) { oat_fh.set_seek_reference(0); oat_fh.seek_begin(); ElfWriter section_headers(oat_version); section_headers.build( isa, oat_size, compute_bss_size_079(single_dex_input)); section_headers.write(oat_fh); } return OatFile::Status::BUILD_SUCCESS; } template <typename DexFileListinType> OatFile::Status build_oatfile_after_v124(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { // Make sure the output is a directory where we will place ODEX and VDEX files CHECK(oat_file_name[oat_file_name.size() - 1] == '/'); OatFile::Status result = OatFile::Status::BUILD_SUCCESS; for (const auto& dex : dex_input) { size_t found = dex.filename.find_last_of('/') + 1; CHECK(found >= 0); auto odex_file_name = dex.filename.substr(found); odex_file_name.erase(odex_file_name.size() - 3); odex_file_name = oat_file_name + odex_file_name + std::string("odex"); CHECK(oat_version == OatVersion::V_124 || oat_version == OatVersion::V_131, "must not build vdex/odex pairs for non-Oreo builds"); auto partial_result = build_vdex_odex_pairs<DexFileListinType>(odex_file_name, oat_version, dex, isa, write_elf, art_image_location, samsung_mode, quick_data); if (partial_result != OatFile::Status::BUILD_SUCCESS) { fprintf(stderr, "Building V124/V131 ODEX/VDEX pair failed for DEX input: %s, " "Result: %d\n", dex.filename.c_str(), static_cast<int>(partial_result)); result = partial_result; } } return result; } OatFile::Status OatFile_064::build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { return build_oatfile<DexFileListing_064, OatClasses_064, LookupTables_Nil, SamsungLookupTables>(oat_file_name, dex_input, oat_version, isa, write_elf, art_image_location, samsung_mode, quick_data); } OatFile::Status OatFile_079::build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { return build_oatfile<DexFileListing_079, OatClasses_079, LookupTables, SamsungLookupTablesNil>(oat_file_name, dex_input, oat_version, isa, write_elf, art_image_location, samsung_mode, quick_data); } OatFile::Status OatFile_124::build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { return build_oatfile_after_v124<DexFileListing_124>(oat_file_name, dex_input, oat_version, isa, write_elf, art_image_location, samsung_mode, quick_data); } OatFile::Status OatFile_131::build(const std::string& oat_file_name, const std::vector<DexInput>& dex_input, const OatVersion oat_version, InstructionSet isa, bool write_elf, const std::string& art_image_location, bool samsung_mode, const QuickData* quick_data) { return build_oatfile_after_v124<DexFileListing_131>(oat_file_name, dex_input, oat_version, isa, write_elf, art_image_location, samsung_mode, quick_data); } OatFile::Status OatFile::build(const std::vector<std::string>& oat_file_names, const std::vector<DexInput>& dex_files, const std::string& oat_version, const std::string& arch, bool write_elf, const std::string& art_image_location, bool samsung_mode, const std::string& quick_data_location) { std::unique_ptr<QuickData> quick_metadata = read_quick_data(quick_data_location); auto build_fn = [&](const std::string& oat_file_name, const std::vector<DexInput>& dexes) { auto version = versionInt(oat_version); auto isa = instruction_set(arch); switch (version) { case OatVersion::V_079: case OatVersion::V_088: return OatFile_079::build(oat_file_name, dexes, version, isa, write_elf, art_image_location, samsung_mode, quick_metadata.get()); case OatVersion::V_039: case OatVersion::V_045: case OatVersion::V_064: case OatVersion::V_067: return OatFile_064::build(oat_file_name, dexes, version, isa, write_elf, art_image_location, samsung_mode, quick_metadata.get()); case OatVersion::V_124: return OatFile_124::build(oat_file_name, dexes, version, isa, write_elf, art_image_location, samsung_mode, quick_metadata.get()); case OatVersion::V_131: return OatFile_131::build(oat_file_name, dexes, version, isa, write_elf, art_image_location, samsung_mode, quick_metadata.get()); default: fprintf(stderr, "version 0x%08x unknown\n", static_cast<int>(version)); return Status::BUILD_UNSUPPORTED_VERSION; } }; if (oat_file_names.empty()) { fprintf(stderr, "At least one oat file name required\n"); return Status::BUILD_ARG_ERROR; } else if (oat_file_names.size() == 1) { return build_fn(oat_file_names[0], dex_files); } else { if (oat_file_names.size() != dex_files.size()) { fprintf(stderr, "One oat file per dex file required.\n"); return Status::BUILD_ARG_ERROR; } for (unsigned int i = 0; i < oat_file_names.size(); i++) { std::vector<DexInput> dex_file; dex_file.push_back(dex_files[i]); auto status = build_fn(oat_file_names[i], dex_file); if (status != Status::BUILD_SUCCESS) { return status; } } return Status::BUILD_SUCCESS; } }