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: At(const void * at)38 static const Header* At(const void* at) { 39 return reinterpret_cast<const Header*>(at); 40 } 41 GetFeatureFlags()42 uint32_t GetFeatureFlags() const { 43 return feature_flags_; 44 } 45 GetDataOffset()46 uint32_t GetDataOffset() const { 47 return data_off_; 48 } 49 GetDataSize()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. OwnedDataBegin()56 uint32_t OwnedDataBegin() const { 57 return owned_data_begin_; 58 } 59 OwnedDataEnd()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 dex::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; CreateCodeItem118 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 HasPreHeaderCodeItem164 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. HasAnyPreHeaderCodeItem169 ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) { 170 return (insns_count_and_flags & kFlagPreHeaderCombined) != 0; 171 } 172 GetPreHeaderCodeItem173 ALWAYS_INLINE uint16_t* GetPreHeader() { 174 return reinterpret_cast<uint16_t*>(this); 175 } 176 GetPreHeaderCodeItem177 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> DecodeFieldsCodeItem184 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 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 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. GetDequickenedSize()264 size_t GetDequickenedSize() const override { 265 return 64 * MB; 266 } 267 GetHeader()268 const Header& GetHeader() const { 269 return down_cast<const Header&>(DexFile::GetHeader()); 270 } 271 272 bool SupportsDefaultMethods() const override; 273 274 uint32_t GetCodeItemSize(const dex::CodeItem& item) const override; 275 GetDebugInfoOffset(uint32_t dex_method_index)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 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