• 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 "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