/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_RUNTIME_OAT_FILE_H_ #define ART_RUNTIME_OAT_FILE_H_ #include #include #include #include #include "base/array_ref.h" #include "base/compiler_filter.h" #include "base/mutex.h" #include "base/os.h" #include "base/safe_map.h" #include "base/tracking_safe_map.h" #include "class_status.h" #include "dex/dex_file_layout.h" #include "dex/type_lookup_table.h" #include "dex/utf.h" #include "index_bss_mapping.h" #include "mirror/object.h" namespace art { class BitVector; class DexFile; class ClassLoaderContext; class ElfFile; class DexLayoutSections; template class GcRoot; class MemMap; class OatDexFile; class OatHeader; class OatMethodOffsets; class OatQuickMethodHeader; class VdexFile; namespace dex { struct ClassDef; } // namespace dex namespace gc { namespace collector { class FakeOatFile; } // namespace collector } // namespace gc // A special compilation reason to indicate that only the VDEX file is usable. Keep in sync with // `ArtConstants::REASON_VDEX` in artd/binder/com/android/server/art/ArtConstants.aidl. static constexpr const char* kReasonVdex = "vdex"; // OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can // save even one OatMethodOffsets struct, the more complicated encoding // using a bitmap pays for itself since few classes will have 160 // methods. enum class OatClassType : uint8_t { kAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method. kSomeCompiled = 1, // A bitmap of OatMethodOffsets that are present follows the OatClass. kNoneCompiled = 2, // All methods are interpreted so no OatMethodOffsets are necessary. kOatClassMax = 3, }; std::ostream& operator<<(std::ostream& os, OatClassType rhs); class PACKED(4) OatMethodOffsets { public: explicit OatMethodOffsets(uint32_t code_offset = 0) : code_offset_(code_offset) {} ~OatMethodOffsets() {} OatMethodOffsets(const OatMethodOffsets&) = default; OatMethodOffsets& operator=(const OatMethodOffsets&) = default; uint32_t code_offset_; }; // Runtime representation of the OAT file format which holds compiler output. // The class opens an OAT file from storage and maps it to memory, typically with // dlopen and provides access to its internal data structures (see OatWriter for // for more details about the OAT format). // In the process of loading OAT, the class also loads the associated VDEX file // with the input DEX files (see VdexFile for details about the VDEX format). // The raw DEX data are accessible transparently through the OatDexFile objects. class OatFile { public: // Open an oat file. Returns null on failure. // The `dex_filenames` argument, if provided, overrides the dex locations // from oat file when opening the dex files if they are not embedded in the // vdex file. These may differ for cross-compilation (the dex file name is // the host path and dex location is the future path on target) and testing. static OatFile* Open(int zip_fd, const std::string& filename, const std::string& location, bool executable, bool low_4gb, ArrayRef dex_filenames, ArrayRef dex_fds, /*inout*/MemMap* reservation, // Where to load if not null. /*out*/std::string* error_msg); // Helper overload that takes a single dex filename and no reservation. static OatFile* Open(int zip_fd, const std::string& filename, const std::string& location, bool executable, bool low_4gb, const std::string& dex_filename, /*out*/std::string* error_msg) { return Open(zip_fd, filename, location, executable, low_4gb, ArrayRef(&dex_filename, /*size=*/ 1u), /*dex_fds=*/ ArrayRef(), // not currently supported /*reservation=*/ nullptr, error_msg); } // Helper overload that takes no dex filename and no reservation. static OatFile* Open(int zip_fd, const std::string& filename, const std::string& location, bool executable, bool low_4gb, /*out*/std::string* error_msg) { return Open(zip_fd, filename, location, executable, low_4gb, /*dex_filenames=*/ ArrayRef(), /*dex_fds=*/ ArrayRef(), // not currently supported /*reservation=*/ nullptr, error_msg); } // Similar to OatFile::Open(const std::string...), but accepts input vdex and // odex files as file descriptors. We also take zip_fd in case the vdex does not // contain the dex code, and we need to read it from the zip file. static OatFile* Open(int zip_fd, int vdex_fd, int oat_fd, const std::string& oat_location, bool executable, bool low_4gb, ArrayRef dex_filenames, ArrayRef dex_fds, /*inout*/MemMap* reservation, // Where to load if not null. /*out*/std::string* error_msg); // Initialize OatFile instance from an already loaded VdexFile. This assumes // the vdex does not have a dex section and accepts a vector of DexFiles separately. static OatFile* OpenFromVdex(const std::vector& dex_files, std::unique_ptr&& vdex_file, const std::string& location, ClassLoaderContext* context); // Initialize OatFile instance from an already loaded VdexFile. The dex files // will be opened through `zip_fd` or `dex_location` if `zip_fd` is -1. static OatFile* OpenFromVdex(int zip_fd, std::unique_ptr&& vdex_file, const std::string& location, ClassLoaderContext* context, std::string* error_msg); // Return whether the `OatFile` uses a vdex-only file. bool IsBackedByVdexOnly() const; virtual ~OatFile(); bool IsExecutable() const { return is_executable_; } // Indicates whether the oat file was compiled with full debugging capability. bool IsDebuggable() const; CompilerFilter::Filter GetCompilerFilter() const; std::string GetClassLoaderContext() const; const char* GetCompilationReason() const; const std::string& GetLocation() const { return location_; } const OatHeader& GetOatHeader() const; class OatMethod final { public: uint32_t GetCodeOffset() const { return code_offset_; } const void* GetQuickCode() const; // Returns size of quick code. uint32_t GetQuickCodeSize() const; // Returns OatQuickMethodHeader for debugging. Most callers should // use more specific methods such as GetQuickCodeSize. const OatQuickMethodHeader* GetOatQuickMethodHeader() const; uint32_t GetOatQuickMethodHeaderOffset() const; size_t GetFrameSizeInBytes() const; uint32_t GetCoreSpillMask() const; uint32_t GetFpSpillMask() const; const uint8_t* GetVmapTable() const; uint32_t GetVmapTableOffset() const; // Create an OatMethod with offsets relative to the given base address OatMethod(const uint8_t* base, const uint32_t code_offset) : begin_(base), code_offset_(code_offset) { } OatMethod(const OatMethod&) = default; ~OatMethod() {} OatMethod& operator=(const OatMethod&) = default; // A representation of an invalid OatMethod, used when an OatMethod or OatClass can't be found. // See ClassLinker::FindOatMethodFor. static const OatMethod Invalid() { return OatMethod(nullptr, -1); } private: const uint8_t* begin_; uint32_t code_offset_; friend class OatClass; }; class OatClass final { public: ClassStatus GetStatus() const { return status_; } OatClassType GetType() const { return type_; } // Get the OatMethod entry based on its index into the class // defintion. Direct methods come first, followed by virtual // methods. Note that runtime created methods such as miranda // methods are not included. const OatMethod GetOatMethod(uint32_t method_index) const; // Return a pointer to the OatMethodOffsets for the requested // method_index, or null if none is present. Note that most // callers should use GetOatMethod. const OatMethodOffsets* GetOatMethodOffsets(uint32_t method_index) const; // Return the offset from the start of the OatFile to the // OatMethodOffsets for the requested method_index, or 0 if none // is present. Note that most callers should use GetOatMethod. uint32_t GetOatMethodOffsetsOffset(uint32_t method_index) const; // A representation of an invalid OatClass, used when an OatClass can't be found. // See FindOatClass(). static OatClass Invalid() { return OatClass(/* oat_file= */ nullptr, ClassStatus::kErrorUnresolved, OatClassType::kNoneCompiled, /* num_methods= */ 0, /* bitmap_pointer= */ nullptr, /* methods_pointer= */ nullptr); } private: OatClass(const OatFile* oat_file, ClassStatus status, OatClassType type, uint32_t num_methods, const uint32_t* bitmap_pointer, const OatMethodOffsets* methods_pointer); const OatFile* const oat_file_; const ClassStatus status_; const OatClassType type_; const uint32_t num_methods_; const uint32_t* const bitmap_; const OatMethodOffsets* const methods_pointer_; friend class art::OatDexFile; }; // Get the OatDexFile for the given dex_location within this oat file. // If dex_location_checksum is non-null, the OatDexFile will only be // returned if it has a matching checksum. // If error_msg is non-null and no OatDexFile is returned, error_msg will // be updated with a description of why no OatDexFile was returned. const OatDexFile* GetOatDexFile(const char* dex_location, const uint32_t* const dex_location_checksum, /*out*/std::string* error_msg = nullptr) const REQUIRES(!secondary_lookup_lock_); const std::vector& GetOatDexFiles() const { return oat_dex_files_storage_; } size_t Size() const { return End() - Begin(); } bool Contains(const void* p) const { return p >= Begin() && p < End(); } size_t DataBimgRelRoSize() const { return DataBimgRelRoEnd() - DataBimgRelRoBegin(); } size_t BssSize() const { return BssEnd() - BssBegin(); } size_t VdexSize() const { return VdexEnd() - VdexBegin(); } size_t BssMethodsOffset() const { // Note: This is used only for symbolizer and needs to return a valid .bss offset. return (bss_methods_ != nullptr) ? bss_methods_ - BssBegin() : BssRootsOffset(); } size_t BssRootsOffset() const { // Note: This is used only for symbolizer and needs to return a valid .bss offset. return (bss_roots_ != nullptr) ? bss_roots_ - BssBegin() : BssSize(); } size_t DexSize() const { return DexEnd() - DexBegin(); } const uint8_t* Begin() const; const uint8_t* End() const; const uint8_t* DataBimgRelRoBegin() const { return data_bimg_rel_ro_begin_; } const uint8_t* DataBimgRelRoEnd() const { return data_bimg_rel_ro_end_; } const uint8_t* BssBegin() const { return bss_begin_; } const uint8_t* BssEnd() const { return bss_end_; } const uint8_t* VdexBegin() const { return vdex_begin_; } const uint8_t* VdexEnd() const { return vdex_end_; } const uint8_t* DexBegin() const; const uint8_t* DexEnd() const; ArrayRef GetBootImageRelocations() const; ArrayRef GetBssMethods() const; ArrayRef> GetBssGcRoots() const; // Initialize relocation sections (.data.bimg.rel.ro and .bss). void InitializeRelocations() const; // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on // error and sets found to false. static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found); VdexFile* GetVdexFile() const { return vdex_.get(); } // Whether the OatFile embeds the Dex code. bool ContainsDexCode() const { return external_dex_files_.empty(); } // Returns whether an image (e.g. app image) is required to safely execute this OAT file. bool RequiresImage() const; struct BssMappingInfo { const IndexBssMapping* method_bss_mapping = nullptr; const IndexBssMapping* type_bss_mapping = nullptr; const IndexBssMapping* public_type_bss_mapping = nullptr; const IndexBssMapping* package_type_bss_mapping = nullptr; const IndexBssMapping* string_bss_mapping = nullptr; }; ArrayRef GetBcpBssInfo() const { return ArrayRef(bcp_bss_info_); } // Returns the mapping info of `dex_file` if found in the BcpBssInfo, or nullptr otherwise. const BssMappingInfo* FindBcpMappingInfo(const DexFile* dex_file) const; protected: OatFile(const std::string& filename, bool executable); private: // The oat file name. // // The image will embed this to link its associated oat file. const std::string location_; // Pointer to the Vdex file with the Dex files for this Oat file. std::unique_ptr vdex_; // Pointer to OatHeader. const uint8_t* begin_; // Pointer to end of oat region for bounds checking. const uint8_t* end_; // Pointer to the .data.bimg.rel.ro section, if present, otherwise null. const uint8_t* data_bimg_rel_ro_begin_; // Pointer to the end of the .data.bimg.rel.ro section, if present, otherwise null. const uint8_t* data_bimg_rel_ro_end_; // Pointer to the .bss section, if present, otherwise null. uint8_t* bss_begin_; // Pointer to the end of the .bss section, if present, otherwise null. uint8_t* bss_end_; // Pointer to the beginning of the ArtMethod*s in .bss section, if present, otherwise null. uint8_t* bss_methods_; // Pointer to the beginning of the GC roots in .bss section, if present, otherwise null. uint8_t* bss_roots_; // Was this oat_file loaded executable? const bool is_executable_; // Pointer to the .vdex section, if present, otherwise null. uint8_t* vdex_begin_; // Pointer to the end of the .vdex section, if present, otherwise null. uint8_t* vdex_end_; // Owning storage for the OatDexFile objects. std::vector oat_dex_files_storage_; // Mapping info for DexFiles in the BCP. std::vector bcp_bss_info_; // NOTE: We use a std::string_view as the key type to avoid a memory allocation on every // lookup with a const char* key. The std::string_view doesn't own its backing storage, // therefore we're using the OatFile's stored dex location as the backing storage // for keys in oat_dex_files_ and the string_cache_ entries for the backing storage // of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_. using Table = AllocationTrackingSafeMap; // Map each location and canonical location (if different) retrieved from the // oat file to its OatDexFile. This map doesn't change after it's constructed in Setup() // and therefore doesn't need any locking and provides the cheapest dex file lookup // for GetOatDexFile() for a very frequent use case. Never contains a null value. Table oat_dex_files_; // Lock guarding all members needed for secondary lookup in GetOatDexFile(). mutable Mutex secondary_lookup_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // If the primary oat_dex_files_ lookup fails, use a secondary map. This map stores // the results of all previous secondary lookups, whether successful (non-null) or // failed (null). If it doesn't contain an entry we need to calculate the canonical // location and use oat_dex_files_by_canonical_location_. mutable Table secondary_oat_dex_files_ GUARDED_BY(secondary_lookup_lock_); // Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_ // and the lazily initialized oat_dex_files_by_canonical_location_. // NOTE: We're keeping references to contained strings in form of std::string_view and adding // new strings to the end. The adding of a new element must not touch any previously stored // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. mutable std::list string_cache_ GUARDED_BY(secondary_lookup_lock_); // Dex files opened directly from a file referenced from the oat file or specifed // by the `dex_filenames` parameter, in case the OatFile does not embed the dex code. std::vector> external_dex_files_; friend class gc::collector::FakeOatFile; // For modifying begin_ and end_. friend class OatClass; friend class art::OatDexFile; friend class OatDumper; // For GetBase and GetLimit friend class OatFileBackedByVdex; friend class OatFileBase; DISALLOW_COPY_AND_ASSIGN(OatFile); }; // OatDexFile should be an inner class of OatFile. Unfortunately, C++ doesn't // support forward declarations of inner classes, and we want to // forward-declare OatDexFile so that we can store an opaque pointer to an // OatDexFile in DexFile. class OatDexFile final { public: // Opens the DexFile referred to by this OatDexFile from within the containing OatFile. std::unique_ptr OpenDexFile(std::string* error_msg) const; // May return null if the OatDexFile only contains a type lookup table. This case only happens // for the compiler to speed up compilation, or in jitzygote. const OatFile* GetOatFile() const { return oat_file_; } // Returns the size of the DexFile refered to by this OatDexFile. size_t FileSize() const; // Returns original path of DexFile that was the source of this OatDexFile. const std::string& GetDexFileLocation() const { return dex_file_location_; } // Returns the canonical location of DexFile that was the source of this OatDexFile. const std::string& GetCanonicalDexFileLocation() const { return canonical_dex_file_location_; } // Returns checksum of original DexFile that was the source of this OatDexFile; uint32_t GetDexFileLocationChecksum() const { return dex_file_location_checksum_; } // Returns the OatClass for the class specified by the given DexFile class_def_index. OatFile::OatClass GetOatClass(uint16_t class_def_index) const; // Returns the offset to the OatClass information. Most callers should use GetOatClass. uint32_t GetOatClassOffset(uint16_t class_def_index) const; const uint8_t* GetLookupTableData() const { return lookup_table_data_; } const IndexBssMapping* GetMethodBssMapping() const { return method_bss_mapping_; } const IndexBssMapping* GetTypeBssMapping() const { return type_bss_mapping_; } const IndexBssMapping* GetPublicTypeBssMapping() const { return public_type_bss_mapping_; } const IndexBssMapping* GetPackageTypeBssMapping() const { return package_type_bss_mapping_; } const IndexBssMapping* GetStringBssMapping() const { return string_bss_mapping_; } const uint8_t* GetDexFilePointer() const { return dex_file_pointer_; } // Looks up a class definition by its class descriptor. Hash must be // ComputeModifiedUtf8Hash(descriptor). static const dex::ClassDef* FindClassDef(const DexFile& dex_file, const char* descriptor, size_t hash); const TypeLookupTable& GetTypeLookupTable() const { return lookup_table_; } ~OatDexFile(); // Create only with a type lookup table, used by the compiler to speed up compilation. explicit OatDexFile(TypeLookupTable&& lookup_table); // Return the dex layout sections. const DexLayoutSections* GetDexLayoutSections() const { return dex_layout_sections_; } private: OatDexFile(const OatFile* oat_file, const std::string& dex_file_location, const std::string& canonical_dex_file_location, uint32_t dex_file_checksum, const uint8_t* dex_file_pointer, const uint8_t* lookup_table_data, const IndexBssMapping* method_bss_mapping, const IndexBssMapping* type_bss_mapping, const IndexBssMapping* public_type_bss_mapping, const IndexBssMapping* package_type_bss_mapping, const IndexBssMapping* string_bss_mapping, const uint32_t* oat_class_offsets_pointer, const DexLayoutSections* dex_layout_sections); // Create an OatDexFile wrapping an existing DexFile. Will set the OatDexFile // pointer in the DexFile. OatDexFile(const OatFile* oat_file, const uint8_t* dex_file_pointer, uint32_t dex_file_checksum, const std::string& dex_file_location, const std::string& canonical_dex_file_location, const uint8_t* lookup_table_data); bool IsBackedByVdexOnly() const; void InitializeTypeLookupTable(); static void AssertAotCompiler(); const OatFile* const oat_file_ = nullptr; const std::string dex_file_location_; const std::string canonical_dex_file_location_; const uint32_t dex_file_location_checksum_ = 0u; const uint8_t* const dex_file_pointer_ = nullptr; const uint8_t* const lookup_table_data_ = nullptr; const IndexBssMapping* const method_bss_mapping_ = nullptr; const IndexBssMapping* const type_bss_mapping_ = nullptr; const IndexBssMapping* const public_type_bss_mapping_ = nullptr; const IndexBssMapping* const package_type_bss_mapping_ = nullptr; const IndexBssMapping* const string_bss_mapping_ = nullptr; const uint32_t* const oat_class_offsets_pointer_ = nullptr; TypeLookupTable lookup_table_; const DexLayoutSections* const dex_layout_sections_ = nullptr; friend class OatFile; friend class OatFileBase; friend class OatFileBackedByVdex; DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; } // namespace art #endif // ART_RUNTIME_OAT_FILE_H_