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 // Last change: remove code item deduping. 31 static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; 32 33 enum class FeatureFlags : uint32_t { 34 kDefaultMethods = 0x1, 35 }; 36 37 class Header : public DexFile::Header { 38 public: At(const void * at)39 static const Header* At(const void* at) { 40 return reinterpret_cast<const Header*>(at); 41 } 42 GetFeatureFlags()43 uint32_t GetFeatureFlags() const { 44 return feature_flags_; 45 } 46 GetDataOffset()47 uint32_t GetDataOffset() const { 48 return data_off_; 49 } 50 GetDataSize()51 uint32_t GetDataSize() const { 52 return data_size_; 53 } 54 55 // Range of the shared data section owned by the dex file. Owned in this context refers to data 56 // for this DEX that was not deduplicated to another DEX. OwnedDataBegin()57 uint32_t OwnedDataBegin() const { 58 return owned_data_begin_; 59 } 60 OwnedDataEnd()61 uint32_t OwnedDataEnd() const { 62 return owned_data_end_; 63 } 64 65 private: 66 uint32_t feature_flags_ = 0u; 67 68 // Position in the compact dex file for the debug info table data starts. 69 uint32_t debug_info_offsets_pos_ = 0u; 70 71 // Offset into the debug info table data where the lookup table is. 72 uint32_t debug_info_offsets_table_offset_ = 0u; 73 74 // Base offset of where debug info starts in the dex file. 75 uint32_t debug_info_base_ = 0u; 76 77 // Range of the shared data section owned by the dex file. 78 uint32_t owned_data_begin_ = 0u; 79 uint32_t owned_data_end_ = 0u; 80 81 friend class CompactDexFile; 82 friend class CompactDexWriter; 83 }; 84 85 // Like the standard code item except without a debug info offset. Each code item may have a 86 // preheader to encode large methods. In 99% of cases, the preheader is not used. This enables 87 // smaller size with a good fast path case in the accessors. 88 struct CodeItem : public dex::CodeItem { 89 static constexpr size_t kAlignment = sizeof(uint16_t); 90 // Max preheader size in uint16_ts. 91 static constexpr size_t kMaxPreHeaderSize = 6; 92 FieldsOffsetCodeItem93 static constexpr size_t FieldsOffset() { 94 return OFFSETOF_MEMBER(CodeItem, fields_); 95 } 96 InsnsCountAndFlagsOffsetCodeItem97 static constexpr size_t InsnsCountAndFlagsOffset() { 98 return OFFSETOF_MEMBER(CodeItem, insns_count_and_flags_); 99 } 100 InsnsOffsetCodeItem101 static constexpr size_t InsnsOffset() { 102 return OFFSETOF_MEMBER(CodeItem, insns_); 103 } 104 105 static constexpr size_t kRegistersSizeShift = 12; 106 static constexpr size_t kInsSizeShift = 8; 107 static constexpr size_t kOutsSizeShift = 4; 108 static constexpr size_t kTriesSizeSizeShift = 0; 109 static constexpr uint16_t kBitPreHeaderRegistersSize = 0; 110 static constexpr uint16_t kBitPreHeaderInsSize = 1; 111 static constexpr uint16_t kBitPreHeaderOutsSize = 2; 112 static constexpr uint16_t kBitPreHeaderTriesSize = 3; 113 static constexpr uint16_t kBitPreHeaderInsnsSize = 4; 114 static constexpr uint16_t kFlagPreHeaderRegistersSize = 0x1 << kBitPreHeaderRegistersSize; 115 static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << kBitPreHeaderInsSize; 116 static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << kBitPreHeaderOutsSize; 117 static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << kBitPreHeaderTriesSize; 118 static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << kBitPreHeaderInsnsSize; 119 static constexpr size_t kInsnsSizeShift = 5; 120 static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte - kInsnsSizeShift; 121 122 private: 123 CodeItem() = default; 124 125 // Combined preheader flags for fast testing if we need to go slow path. 126 static constexpr uint16_t kFlagPreHeaderCombined = 127 kFlagPreHeaderRegistersSize | 128 kFlagPreHeaderInsSize | 129 kFlagPreHeaderOutsSize | 130 kFlagPreHeaderTriesSize | 131 kFlagPreHeaderInsnsSize; 132 133 // Create a code item and associated preheader if required based on field values. 134 // Returns the start of the preheader. The preheader buffer must be at least as large as 135 // kMaxPreHeaderSize; CreateCodeItem136 uint16_t* Create(uint16_t registers_size, 137 uint16_t ins_size, 138 uint16_t outs_size, 139 uint16_t tries_size, 140 uint32_t insns_size_in_code_units, 141 uint16_t* out_preheader) { 142 // Dex verification ensures that registers size > ins_size, so we can subtract the registers 143 // size accordingly to reduce how often we need to use the preheader. 144 DCHECK_GE(registers_size, ins_size); 145 registers_size -= ins_size; 146 fields_ = (registers_size & 0xF) << kRegistersSizeShift; 147 fields_ |= (ins_size & 0xF) << kInsSizeShift; 148 fields_ |= (outs_size & 0xF) << kOutsSizeShift; 149 fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift; 150 registers_size &= ~0xF; 151 ins_size &= ~0xF; 152 outs_size &= ~0xF; 153 tries_size &= ~0xF; 154 insns_count_and_flags_ = 0; 155 const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1); 156 insns_count_and_flags_ |= masked_count << kInsnsSizeShift; 157 insns_size_in_code_units -= masked_count; 158 159 // Since the preheader case is rare (1% of code items), use a suboptimally large but fast 160 // decoding format. 161 if (insns_size_in_code_units != 0) { 162 insns_count_and_flags_ |= kFlagPreHeaderInsnsSize; 163 --out_preheader; 164 *out_preheader = static_cast<uint16_t>(insns_size_in_code_units); 165 --out_preheader; 166 *out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16); 167 } 168 auto preheader_encode = [&](uint16_t size, uint16_t flag) { 169 if (size != 0) { 170 insns_count_and_flags_ |= flag; 171 --out_preheader; 172 *out_preheader = size; 173 } 174 }; 175 preheader_encode(registers_size, kFlagPreHeaderRegistersSize); 176 preheader_encode(ins_size, kFlagPreHeaderInsSize); 177 preheader_encode(outs_size, kFlagPreHeaderOutsSize); 178 preheader_encode(tries_size, kFlagPreHeaderTriesSize); 179 return out_preheader; 180 } 181 HasPreHeaderCodeItem182 ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const { 183 return (insns_count_and_flags_ & flag) != 0; 184 } 185 186 // Return true if the code item has any preheaders. HasAnyPreHeaderCodeItem187 ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) { 188 return (insns_count_and_flags & kFlagPreHeaderCombined) != 0; 189 } 190 GetPreHeaderCodeItem191 ALWAYS_INLINE uint16_t* GetPreHeader() { 192 return reinterpret_cast<uint16_t*>(this); 193 } 194 GetPreHeaderCodeItem195 ALWAYS_INLINE const uint16_t* GetPreHeader() const { 196 return reinterpret_cast<const uint16_t*>(this); 197 } 198 199 // Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is 200 // specified then only the instruction count is decoded. 201 template <bool kDecodeOnlyInstructionCount> DecodeFieldsCodeItem202 ALWAYS_INLINE void DecodeFields(uint32_t* insns_count, 203 uint16_t* registers_size, 204 uint16_t* ins_size, 205 uint16_t* outs_size, 206 uint16_t* tries_size) const { 207 *insns_count = insns_count_and_flags_ >> kInsnsSizeShift; 208 if (!kDecodeOnlyInstructionCount) { 209 const uint16_t fields = fields_; 210 *registers_size = (fields >> kRegistersSizeShift) & 0xF; 211 *ins_size = (fields >> kInsSizeShift) & 0xF; 212 *outs_size = (fields >> kOutsSizeShift) & 0xF; 213 *tries_size = (fields >> kTriesSizeSizeShift) & 0xF; 214 } 215 if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) { 216 const uint16_t* preheader = GetPreHeader(); 217 if (HasPreHeader(kFlagPreHeaderInsnsSize)) { 218 --preheader; 219 *insns_count += static_cast<uint32_t>(*preheader); 220 --preheader; 221 *insns_count += static_cast<uint32_t>(*preheader) << 16; 222 } 223 if (!kDecodeOnlyInstructionCount) { 224 if (HasPreHeader(kFlagPreHeaderRegistersSize)) { 225 --preheader; 226 *registers_size += preheader[0]; 227 } 228 if (HasPreHeader(kFlagPreHeaderInsSize)) { 229 --preheader; 230 *ins_size += preheader[0]; 231 } 232 if (HasPreHeader(kFlagPreHeaderOutsSize)) { 233 --preheader; 234 *outs_size += preheader[0]; 235 } 236 if (HasPreHeader(kFlagPreHeaderTriesSize)) { 237 --preheader; 238 *tries_size += preheader[0]; 239 } 240 } 241 } 242 if (!kDecodeOnlyInstructionCount) { 243 *registers_size += *ins_size; 244 } 245 } 246 247 // Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size] 248 uint16_t fields_; 249 250 // 5 bits for if either of the fields required preheader extension, 11 bits for the number of 251 // instruction code units. 252 uint16_t insns_count_and_flags_; 253 254 uint16_t insns_[1]; // actual array of bytecode. 255 256 ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); 257 ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields); 258 friend class CodeItemDataAccessor; 259 friend class CodeItemDebugInfoAccessor; 260 friend class CodeItemInstructionAccessor; 261 friend class CompactDexFile; 262 friend class CompactDexWriter; 263 DISALLOW_COPY_AND_ASSIGN(CodeItem); 264 }; 265 266 // Write the compact dex specific magic. 267 static void WriteMagic(uint8_t* magic); 268 269 // Write the current version, note that the input is the address of the magic. 270 static void WriteCurrentVersion(uint8_t* magic); 271 272 // Returns true if the byte string points to the magic value. 273 static bool IsMagicValid(const uint8_t* magic); 274 bool IsMagicValid() const override; 275 276 // Returns true if the byte string after the magic is the correct value. 277 static bool IsVersionValid(const uint8_t* magic); 278 bool IsVersionValid() const override; 279 280 // TODO This is completely a guess. We really need to do better. b/72402467 281 // We ask for 64 megabytes which should be big enough for any realistic dex file. GetDequickenedSize()282 size_t GetDequickenedSize() const override { 283 return 64 * MB; 284 } 285 GetHeader()286 const Header& GetHeader() const { 287 return down_cast<const Header&>(DexFile::GetHeader()); 288 } 289 290 bool SupportsDefaultMethods() const override; 291 292 uint32_t GetCodeItemSize(const dex::CodeItem& item) const override; 293 GetDebugInfoOffset(uint32_t dex_method_index)294 uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const { 295 return debug_info_offsets_.GetOffset(dex_method_index); 296 } 297 298 static uint32_t CalculateChecksum(const uint8_t* base_begin, 299 size_t base_size, 300 const uint8_t* data_begin, 301 size_t data_size); 302 uint32_t CalculateChecksum() const override; 303 304 private: 305 CompactDexFile(const uint8_t* base, 306 size_t size, 307 const uint8_t* data_begin, 308 size_t data_size, 309 const std::string& location, 310 uint32_t location_checksum, 311 const OatDexFile* oat_dex_file, 312 std::unique_ptr<DexFileContainer> container); 313 314 CompactOffsetTable::Accessor debug_info_offsets_; 315 316 friend class DexFile; 317 friend class DexFileLoader; 318 DISALLOW_COPY_AND_ASSIGN(CompactDexFile); 319 }; 320 321 } // namespace art 322 323 #endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_ 324