Home | History | Annotate | Download | only in dex
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
     18 #define ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
     19 
     20 #include "base/casts.h"
     21 #include "dex_file.h"
     22 #include "dex/compact_offset_table.h"
     23 
     24 namespace art {
     25 
     26 // CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage.
     27 class CompactDexFile : public DexFile {
     28  public:
     29   static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
     30   static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
     31 
     32   enum class FeatureFlags : uint32_t {
     33     kDefaultMethods = 0x1,
     34   };
     35 
     36   class Header : public DexFile::Header {
     37    public:
     38     static const Header* At(const void* at) {
     39       return reinterpret_cast<const Header*>(at);
     40     }
     41 
     42     uint32_t GetFeatureFlags() const {
     43       return feature_flags_;
     44     }
     45 
     46     uint32_t GetDataOffset() const {
     47       return data_off_;
     48     }
     49 
     50     uint32_t GetDataSize() const {
     51       return data_size_;
     52     }
     53 
     54     // Range of the shared data section owned by the dex file. Owned in this context refers to data
     55     // for this DEX that was not deduplicated to another DEX.
     56     uint32_t OwnedDataBegin() const {
     57       return owned_data_begin_;
     58     }
     59 
     60     uint32_t OwnedDataEnd() const {
     61       return owned_data_end_;
     62     }
     63 
     64    private:
     65     uint32_t feature_flags_ = 0u;
     66 
     67     // Position in the compact dex file for the debug info table data starts.
     68     uint32_t debug_info_offsets_pos_ = 0u;
     69 
     70     // Offset into the debug info table data where the lookup table is.
     71     uint32_t debug_info_offsets_table_offset_ = 0u;
     72 
     73     // Base offset of where debug info starts in the dex file.
     74     uint32_t debug_info_base_ = 0u;
     75 
     76     // Range of the shared data section owned by the dex file.
     77     uint32_t owned_data_begin_ = 0u;
     78     uint32_t owned_data_end_ = 0u;
     79 
     80     friend class CompactDexFile;
     81     friend class CompactDexWriter;
     82   };
     83 
     84   // Like the standard code item except without a debug info offset. Each code item may have a
     85   // preheader to encode large methods. In 99% of cases, the preheader is not used. This enables
     86   // smaller size with a good fast path case in the accessors.
     87   struct CodeItem : public DexFile::CodeItem {
     88     static constexpr size_t kAlignment = sizeof(uint16_t);
     89     // Max preheader size in uint16_ts.
     90     static constexpr size_t kMaxPreHeaderSize = 6;
     91 
     92    private:
     93     CodeItem() = default;
     94 
     95     static constexpr size_t kRegistersSizeShift = 12;
     96     static constexpr size_t kInsSizeShift = 8;
     97     static constexpr size_t kOutsSizeShift = 4;
     98     static constexpr size_t kTriesSizeSizeShift = 0;
     99     static constexpr uint16_t kFlagPreHeaderRegisterSize = 0x1 << 0;
    100     static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << 1;
    101     static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << 2;
    102     static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << 3;
    103     static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << 4;
    104     static constexpr size_t kInsnsSizeShift = 5;
    105     static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte -  kInsnsSizeShift;
    106 
    107     // Combined preheader flags for fast testing if we need to go slow path.
    108     static constexpr uint16_t kFlagPreHeaderCombined =
    109         kFlagPreHeaderRegisterSize |
    110         kFlagPreHeaderInsSize |
    111         kFlagPreHeaderOutsSize |
    112         kFlagPreHeaderTriesSize |
    113         kFlagPreHeaderInsnsSize;
    114 
    115     // Create a code item and associated preheader if required based on field values.
    116     // Returns the start of the preheader. The preheader buffer must be at least as large as
    117     // kMaxPreHeaderSize;
    118     uint16_t* Create(uint16_t registers_size,
    119                      uint16_t ins_size,
    120                      uint16_t outs_size,
    121                      uint16_t tries_size,
    122                      uint32_t insns_size_in_code_units,
    123                      uint16_t* out_preheader) {
    124       // Dex verification ensures that registers size > ins_size, so we can subtract the registers
    125       // size accordingly to reduce how often we need to use the preheader.
    126       DCHECK_GE(registers_size, ins_size);
    127       registers_size -= ins_size;
    128       fields_ = (registers_size & 0xF) << kRegistersSizeShift;
    129       fields_ |= (ins_size & 0xF) << kInsSizeShift;
    130       fields_ |= (outs_size & 0xF) << kOutsSizeShift;
    131       fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift;
    132       registers_size &= ~0xF;
    133       ins_size &= ~0xF;
    134       outs_size &= ~0xF;
    135       tries_size &= ~0xF;
    136       insns_count_and_flags_ = 0;
    137       const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1);
    138       insns_count_and_flags_ |= masked_count << kInsnsSizeShift;
    139       insns_size_in_code_units -= masked_count;
    140 
    141       // Since the preheader case is rare (1% of code items), use a suboptimally large but fast
    142       // decoding format.
    143       if (insns_size_in_code_units != 0) {
    144         insns_count_and_flags_ |= kFlagPreHeaderInsnsSize;
    145         --out_preheader;
    146         *out_preheader = static_cast<uint16_t>(insns_size_in_code_units);
    147         --out_preheader;
    148         *out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16);
    149       }
    150       auto preheader_encode = [&](uint16_t size, uint16_t flag) {
    151         if (size != 0) {
    152           insns_count_and_flags_ |= flag;
    153           --out_preheader;
    154           *out_preheader = size;
    155         }
    156       };
    157       preheader_encode(registers_size, kFlagPreHeaderRegisterSize);
    158       preheader_encode(ins_size, kFlagPreHeaderInsSize);
    159       preheader_encode(outs_size, kFlagPreHeaderOutsSize);
    160       preheader_encode(tries_size, kFlagPreHeaderTriesSize);
    161       return out_preheader;
    162     }
    163 
    164     ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const {
    165       return (insns_count_and_flags_ & flag) != 0;
    166     }
    167 
    168     // Return true if the code item has any preheaders.
    169     ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) {
    170       return (insns_count_and_flags & kFlagPreHeaderCombined) != 0;
    171     }
    172 
    173     ALWAYS_INLINE uint16_t* GetPreHeader() {
    174       return reinterpret_cast<uint16_t*>(this);
    175     }
    176 
    177     ALWAYS_INLINE const uint16_t* GetPreHeader() const {
    178       return reinterpret_cast<const uint16_t*>(this);
    179     }
    180 
    181     // Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is
    182     // specified then only the instruction count is decoded.
    183     template <bool kDecodeOnlyInstructionCount>
    184     ALWAYS_INLINE void DecodeFields(uint32_t* insns_count,
    185                                     uint16_t* registers_size,
    186                                     uint16_t* ins_size,
    187                                     uint16_t* outs_size,
    188                                     uint16_t* tries_size) const {
    189       *insns_count = insns_count_and_flags_ >> kInsnsSizeShift;
    190       if (!kDecodeOnlyInstructionCount) {
    191         const uint16_t fields = fields_;
    192         *registers_size = (fields >> kRegistersSizeShift) & 0xF;
    193         *ins_size = (fields >> kInsSizeShift) & 0xF;
    194         *outs_size = (fields >> kOutsSizeShift) & 0xF;
    195         *tries_size = (fields >> kTriesSizeSizeShift) & 0xF;
    196       }
    197       if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) {
    198         const uint16_t* preheader = GetPreHeader();
    199         if (HasPreHeader(kFlagPreHeaderInsnsSize)) {
    200           --preheader;
    201           *insns_count += static_cast<uint32_t>(*preheader);
    202           --preheader;
    203           *insns_count += static_cast<uint32_t>(*preheader) << 16;
    204         }
    205         if (!kDecodeOnlyInstructionCount) {
    206           if (HasPreHeader(kFlagPreHeaderRegisterSize)) {
    207             --preheader;
    208             *registers_size += preheader[0];
    209           }
    210           if (HasPreHeader(kFlagPreHeaderInsSize)) {
    211             --preheader;
    212             *ins_size += preheader[0];
    213           }
    214           if (HasPreHeader(kFlagPreHeaderOutsSize)) {
    215             --preheader;
    216             *outs_size += preheader[0];
    217           }
    218           if (HasPreHeader(kFlagPreHeaderTriesSize)) {
    219             --preheader;
    220             *tries_size += preheader[0];
    221           }
    222         }
    223       }
    224       if (!kDecodeOnlyInstructionCount) {
    225         *registers_size += *ins_size;
    226       }
    227     }
    228 
    229     // Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size]
    230     uint16_t fields_;
    231 
    232     // 5 bits for if either of the fields required preheader extension, 11 bits for the number of
    233     // instruction code units.
    234     uint16_t insns_count_and_flags_;
    235 
    236     uint16_t insns_[1];                  // actual array of bytecode.
    237 
    238     ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
    239     ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields);
    240     friend class CodeItemDataAccessor;
    241     friend class CodeItemDebugInfoAccessor;
    242     friend class CodeItemInstructionAccessor;
    243     friend class CompactDexFile;
    244     friend class CompactDexWriter;
    245     DISALLOW_COPY_AND_ASSIGN(CodeItem);
    246   };
    247 
    248   // Write the compact dex specific magic.
    249   static void WriteMagic(uint8_t* magic);
    250 
    251   // Write the current version, note that the input is the address of the magic.
    252   static void WriteCurrentVersion(uint8_t* magic);
    253 
    254   // Returns true if the byte string points to the magic value.
    255   static bool IsMagicValid(const uint8_t* magic);
    256   virtual bool IsMagicValid() const OVERRIDE;
    257 
    258   // Returns true if the byte string after the magic is the correct value.
    259   static bool IsVersionValid(const uint8_t* magic);
    260   virtual bool IsVersionValid() const OVERRIDE;
    261 
    262   // TODO This is completely a guess. We really need to do better. b/72402467
    263   // We ask for 64 megabytes which should be big enough for any realistic dex file.
    264   virtual size_t GetDequickenedSize() const OVERRIDE {
    265     return 64 * MB;
    266   }
    267 
    268   const Header& GetHeader() const {
    269     return down_cast<const Header&>(DexFile::GetHeader());
    270   }
    271 
    272   virtual bool SupportsDefaultMethods() const OVERRIDE;
    273 
    274   uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
    275 
    276   uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
    277     return debug_info_offsets_.GetOffset(dex_method_index);
    278   }
    279 
    280   static uint32_t CalculateChecksum(const uint8_t* base_begin,
    281                                     size_t base_size,
    282                                     const uint8_t* data_begin,
    283                                     size_t data_size);
    284   virtual uint32_t CalculateChecksum() const OVERRIDE;
    285 
    286  private:
    287   CompactDexFile(const uint8_t* base,
    288                  size_t size,
    289                  const uint8_t* data_begin,
    290                  size_t data_size,
    291                  const std::string& location,
    292                  uint32_t location_checksum,
    293                  const OatDexFile* oat_dex_file,
    294                  std::unique_ptr<DexFileContainer> container);
    295 
    296   CompactOffsetTable::Accessor debug_info_offsets_;
    297 
    298   friend class DexFile;
    299   friend class DexFileLoader;
    300   DISALLOW_COPY_AND_ASSIGN(CompactDexFile);
    301 };
    302 
    303 }  // namespace art
    304 
    305 #endif  // ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
    306