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