blob: 73eb39bc7b209d31451edecdeac0061c1170765c [file] [log] [blame]
Mathieu Chartierf95a75e2017-11-03 15:25:52 -07001/*
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#include "compact_dex_writer.h"
18
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080019#include "base/logging.h"
20#include "base/time_utils.h"
21#include "dex/compact_dex_debug_info.h"
David Sehr9e734c72018-01-04 17:56:19 -080022#include "dex/compact_dex_file.h"
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080023#include "dexlayout.h"
Mathieu Chartierf95a75e2017-11-03 15:25:52 -070024
25namespace art {
26
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080027uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(uint32_t offset) {
28 const uint32_t start_offset = offset;
29 const dex_ir::Collections& collections = header_->GetCollections();
30 // Debug offsets for method indexes. 0 means no debug info.
31 std::vector<uint32_t> debug_info_offsets(collections.MethodIdsSize(), 0u);
32
33 static constexpr InvokeType invoke_types[] = {
34 kDirect,
35 kVirtual
36 };
37
38 for (InvokeType invoke_type : invoke_types) {
39 for (const std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) {
40 // Skip classes that are not defined in this dex file.
41 dex_ir::ClassData* class_data = class_def->GetClassData();
42 if (class_data == nullptr) {
43 continue;
44 }
45 for (auto& method : *(invoke_type == InvokeType::kDirect
46 ? class_data->DirectMethods()
47 : class_data->VirtualMethods())) {
48 const dex_ir::MethodId* method_id = method->GetMethodId();
49 dex_ir::CodeItem* code_item = method->GetCodeItem();
50 if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
51 const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset();
52 const uint32_t method_idx = method_id->GetIndex();
53 if (debug_info_offsets[method_idx] != 0u) {
54 CHECK_EQ(debug_info_offset, debug_info_offsets[method_idx]);
55 }
56 debug_info_offsets[method_idx] = debug_info_offset;
57 }
58 }
59 }
60 }
61
62 std::vector<uint8_t> data;
63 debug_info_base_ = 0u;
64 debug_info_offsets_table_offset_ = 0u;
65 CompactDexDebugInfoOffsetTable::Build(debug_info_offsets,
66 &data,
67 &debug_info_base_,
68 &debug_info_offsets_table_offset_);
69 // Align the table and write it out.
70 offset = RoundUp(offset, CompactDexDebugInfoOffsetTable::kAlignment);
71 debug_info_offsets_pos_ = offset;
72 offset += Write(data.data(), data.size(), offset);
73
74 // Verify that the whole table decodes as expected and measure average performance.
75 const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_;
76 if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
77 uint64_t start_time = NanoTime();
78 CompactDexDebugInfoOffsetTable::Accessor accessor(mem_map_->Begin() + debug_info_offsets_pos_,
79 debug_info_base_,
80 debug_info_offsets_table_offset_);
81
82 for (size_t i = 0; i < debug_info_offsets.size(); ++i) {
83 CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]);
84 }
85 uint64_t end_time = NanoTime();
86 VLOG(dex) << "Average lookup time (ns) for debug info offsets: "
87 << (end_time - start_time) / debug_info_offsets.size();
88 }
89
90 return offset - start_offset;
91}
92
93uint32_t CompactDexWriter::WriteCodeItem(dex_ir::CodeItem* code_item,
94 uint32_t offset,
95 bool reserve_only) {
96 DCHECK(code_item != nullptr);
97 const uint32_t start_offset = offset;
Mathieu Chartier8740c662018-01-11 14:50:02 -080098 // Use 4 byte alignment for now, we need to peek at the bytecode to see if we can 2 byte align
99 // otherwise.
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800100 offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800101
102 CompactDexFile::CodeItem disk_code_item;
Mathieu Chartier8740c662018-01-11 14:50:02 -0800103
104 uint16_t preheader_storage[CompactDexFile::CodeItem::kMaxPreHeaderSize] = {};
105 uint16_t* preheader_end = preheader_storage + CompactDexFile::CodeItem::kMaxPreHeaderSize;
106 const uint16_t* preheader = disk_code_item.Create(
107 code_item->RegistersSize(),
108 code_item->InsSize(),
109 code_item->OutsSize(),
110 code_item->TriesSize(),
111 code_item->InsnsSize(),
112 preheader_end);
113 const size_t preheader_bytes = (preheader_end - preheader) * sizeof(preheader[0]);
114
115 static constexpr size_t kPayloadInstructionRequiredAlignment = 4;
116 const uint32_t current_code_item_start = offset + preheader_bytes;
117 if (!IsAlignedParam(current_code_item_start, kPayloadInstructionRequiredAlignment)) {
118 // If the preheader is going to make the code unaligned, consider adding 2 bytes of padding
119 // before if required.
120 for (const DexInstructionPcPair& instruction : code_item->Instructions()) {
121 const Instruction::Code opcode = instruction->Opcode();
122 // Payload instructions possibly require special alignment for their data.
123 if (opcode == Instruction::FILL_ARRAY_DATA ||
124 opcode == Instruction::PACKED_SWITCH ||
125 opcode == Instruction::SPARSE_SWITCH) {
126 offset += RoundUp(current_code_item_start, kPayloadInstructionRequiredAlignment) -
127 current_code_item_start;
128 break;
129 }
130 }
131 }
132
133 // Write preheader first.
134 offset += Write(reinterpret_cast<const uint8_t*>(preheader), preheader_bytes, offset);
135 // Registered offset is after the preheader.
136 ProcessOffset(&offset, code_item);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800137 // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
138 // item.
139 offset += Write(&disk_code_item,
140 OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_),
141 offset);
142 // Write the instructions.
143 offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
144 // Write the post instruction data.
145 offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
146 return offset - start_offset;
147}
148
149void CompactDexWriter::SortDebugInfosByMethodIndex() {
150 dex_ir::Collections& collections = header_->GetCollections();
151 static constexpr InvokeType invoke_types[] = {
152 kDirect,
153 kVirtual
154 };
155 std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map;
156 for (InvokeType invoke_type : invoke_types) {
157 for (std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) {
158 // Skip classes that are not defined in this dex file.
159 dex_ir::ClassData* class_data = class_def->GetClassData();
160 if (class_data == nullptr) {
161 continue;
162 }
163 for (auto& method : *(invoke_type == InvokeType::kDirect
164 ? class_data->DirectMethods()
165 : class_data->VirtualMethods())) {
166 const dex_ir::MethodId* method_id = method->GetMethodId();
167 dex_ir::CodeItem* code_item = method->GetCodeItem();
168 if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
169 const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
170 method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
171 }
172 }
173 }
174 }
175 std::sort(collections.DebugInfoItems().begin(),
176 collections.DebugInfoItems().end(),
177 [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a,
178 const std::unique_ptr<dex_ir::DebugInfoItem>& b) {
179 auto it_a = method_idx_map.find(a.get());
180 auto it_b = method_idx_map.find(b.get());
181 uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u;
182 uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u;
183 return idx_a < idx_b;
184 });
185}
186
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700187void CompactDexWriter::WriteHeader() {
188 CompactDexFile::Header header;
189 CompactDexFile::WriteMagic(&header.magic_[0]);
190 CompactDexFile::WriteCurrentVersion(&header.magic_[0]);
191 header.checksum_ = header_->Checksum();
192 std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_);
193 header.file_size_ = header_->FileSize();
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800194 // Since we are not necessarily outputting the same format as the input, avoid using the stored
195 // header size.
196 header.header_size_ = GetHeaderSize();
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700197 header.endian_tag_ = header_->EndianTag();
198 header.link_size_ = header_->LinkSize();
199 header.link_off_ = header_->LinkOffset();
200 const dex_ir::Collections& collections = header_->GetCollections();
201 header.map_off_ = collections.MapListOffset();
202 header.string_ids_size_ = collections.StringIdsSize();
203 header.string_ids_off_ = collections.StringIdsOffset();
204 header.type_ids_size_ = collections.TypeIdsSize();
205 header.type_ids_off_ = collections.TypeIdsOffset();
206 header.proto_ids_size_ = collections.ProtoIdsSize();
207 header.proto_ids_off_ = collections.ProtoIdsOffset();
208 header.field_ids_size_ = collections.FieldIdsSize();
209 header.field_ids_off_ = collections.FieldIdsOffset();
210 header.method_ids_size_ = collections.MethodIdsSize();
211 header.method_ids_off_ = collections.MethodIdsOffset();
212 header.class_defs_size_ = collections.ClassDefsSize();
213 header.class_defs_off_ = collections.ClassDefsOffset();
214 header.data_size_ = header_->DataSize();
215 header.data_off_ = header_->DataOffset();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800216
217 // Compact dex specific flags.
218 header.debug_info_offsets_pos_ = debug_info_offsets_pos_;
219 header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_;
220 header.debug_info_base_ = debug_info_base_;
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800221 header.feature_flags_ = 0u;
222 // In cases where apps are converted to cdex during install, maintain feature flags so that
223 // the verifier correctly verifies apps that aren't targetting default methods.
224 if (header_->SupportDefaultMethods()) {
225 header.feature_flags_ |= static_cast<uint32_t>(CompactDexFile::FeatureFlags::kDefaultMethods);
226 }
Mathieu Chartier3e0c5172017-11-12 12:58:40 -0800227 UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u));
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700228}
229
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800230size_t CompactDexWriter::GetHeaderSize() const {
231 return sizeof(CompactDexFile::Header);
232}
233
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800234void CompactDexWriter::WriteMemMap() {
235 // Starting offset is right after the header.
236 uint32_t offset = GetHeaderSize();
237
238 dex_ir::Collections& collection = header_->GetCollections();
239
240 // Based on: https://source.android.com/devices/tech/dalvik/dex-format
241 // Since the offsets may not be calculated already, the writing must be done in the correct order.
242 const uint32_t string_ids_offset = offset;
243 offset += WriteStringIds(offset, /*reserve_only*/ true);
244 offset += WriteTypeIds(offset);
245 const uint32_t proto_ids_offset = offset;
246 offset += WriteProtoIds(offset, /*reserve_only*/ true);
247 offset += WriteFieldIds(offset);
248 offset += WriteMethodIds(offset);
249 const uint32_t class_defs_offset = offset;
250 offset += WriteClassDefs(offset, /*reserve_only*/ true);
251 const uint32_t call_site_ids_offset = offset;
252 offset += WriteCallSiteIds(offset, /*reserve_only*/ true);
253 offset += WriteMethodHandles(offset);
254
255 uint32_t data_offset_ = 0u;
256 if (compute_offsets_) {
257 // Data section.
258 offset = RoundUp(offset, kDataSectionAlignment);
259 data_offset_ = offset;
260 }
261
262 // Write code item first to minimize the space required for encoded methods.
263 // For cdex, the code items don't depend on the debug info.
264 offset += WriteCodeItems(offset, /*reserve_only*/ false);
265
266 // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of
267 // the debug info offset table.
268 SortDebugInfosByMethodIndex();
269 offset += WriteDebugInfoItems(offset);
270
271 offset += WriteEncodedArrays(offset);
272 offset += WriteAnnotations(offset);
273 offset += WriteAnnotationSets(offset);
274 offset += WriteAnnotationSetRefs(offset);
275 offset += WriteAnnotationsDirectories(offset);
276 offset += WriteTypeLists(offset);
277 offset += WriteClassDatas(offset);
278 offset += WriteStringDatas(offset);
279
280 // Write delayed id sections that depend on data sections.
281 WriteStringIds(string_ids_offset, /*reserve_only*/ false);
282 WriteProtoIds(proto_ids_offset, /*reserve_only*/ false);
283 WriteClassDefs(class_defs_offset, /*reserve_only*/ false);
284 WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false);
285
286 // Write the map list.
287 if (compute_offsets_) {
288 offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList));
289 collection.SetMapListOffset(offset);
290 } else {
291 offset = collection.MapListOffset();
292 }
293 offset += GenerateAndWriteMapItems(offset);
294 offset = RoundUp(offset, kDataSectionAlignment);
295
296 // Map items are included in the data section.
297 if (compute_offsets_) {
298 header_->SetDataSize(offset - data_offset_);
299 if (header_->DataSize() != 0) {
300 // Offset must be zero when the size is zero.
301 header_->SetDataOffset(data_offset_);
302 } else {
303 header_->SetDataOffset(0u);
304 }
305 }
306
307 // Write link data if it exists.
308 const std::vector<uint8_t>& link_data = collection.LinkData();
309 if (link_data.size() > 0) {
310 CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
311 if (compute_offsets_) {
312 header_->SetLinkOffset(offset);
313 }
314 offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
315 }
316
317 // Write debug info offset table last to make dex file verifier happy.
318 offset += WriteDebugInfoOffsetTable(offset);
319
320 // Write header last.
321 if (compute_offsets_) {
322 header_->SetFileSize(offset);
323 }
324 WriteHeader();
325
326 if (dex_layout_->GetOptions().update_checksum_) {
327 header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset));
328 // Rewrite the header with the calculated checksum.
329 WriteHeader();
330 }
331}
332
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700333} // namespace art