blob: d1dc6587c0f5d65ccf8e9807b9d4ad6701e74b9f [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;
98 offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment);
99 ProcessOffset(&offset, code_item);
100
101 CompactDexFile::CodeItem disk_code_item;
102 disk_code_item.registers_size_ = code_item->RegistersSize();
103 disk_code_item.ins_size_ = code_item->InsSize();
104 disk_code_item.outs_size_ = code_item->OutsSize();
105 disk_code_item.tries_size_ = code_item->TriesSize();
106 disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize();
107 // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
108 // item.
109 offset += Write(&disk_code_item,
110 OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_),
111 offset);
112 // Write the instructions.
113 offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
114 // Write the post instruction data.
115 offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
116 return offset - start_offset;
117}
118
119void CompactDexWriter::SortDebugInfosByMethodIndex() {
120 dex_ir::Collections& collections = header_->GetCollections();
121 static constexpr InvokeType invoke_types[] = {
122 kDirect,
123 kVirtual
124 };
125 std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map;
126 for (InvokeType invoke_type : invoke_types) {
127 for (std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) {
128 // Skip classes that are not defined in this dex file.
129 dex_ir::ClassData* class_data = class_def->GetClassData();
130 if (class_data == nullptr) {
131 continue;
132 }
133 for (auto& method : *(invoke_type == InvokeType::kDirect
134 ? class_data->DirectMethods()
135 : class_data->VirtualMethods())) {
136 const dex_ir::MethodId* method_id = method->GetMethodId();
137 dex_ir::CodeItem* code_item = method->GetCodeItem();
138 if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
139 const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
140 method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
141 }
142 }
143 }
144 }
145 std::sort(collections.DebugInfoItems().begin(),
146 collections.DebugInfoItems().end(),
147 [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a,
148 const std::unique_ptr<dex_ir::DebugInfoItem>& b) {
149 auto it_a = method_idx_map.find(a.get());
150 auto it_b = method_idx_map.find(b.get());
151 uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u;
152 uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u;
153 return idx_a < idx_b;
154 });
155}
156
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700157void CompactDexWriter::WriteHeader() {
158 CompactDexFile::Header header;
159 CompactDexFile::WriteMagic(&header.magic_[0]);
160 CompactDexFile::WriteCurrentVersion(&header.magic_[0]);
161 header.checksum_ = header_->Checksum();
162 std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_);
163 header.file_size_ = header_->FileSize();
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800164 // Since we are not necessarily outputting the same format as the input, avoid using the stored
165 // header size.
166 header.header_size_ = GetHeaderSize();
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700167 header.endian_tag_ = header_->EndianTag();
168 header.link_size_ = header_->LinkSize();
169 header.link_off_ = header_->LinkOffset();
170 const dex_ir::Collections& collections = header_->GetCollections();
171 header.map_off_ = collections.MapListOffset();
172 header.string_ids_size_ = collections.StringIdsSize();
173 header.string_ids_off_ = collections.StringIdsOffset();
174 header.type_ids_size_ = collections.TypeIdsSize();
175 header.type_ids_off_ = collections.TypeIdsOffset();
176 header.proto_ids_size_ = collections.ProtoIdsSize();
177 header.proto_ids_off_ = collections.ProtoIdsOffset();
178 header.field_ids_size_ = collections.FieldIdsSize();
179 header.field_ids_off_ = collections.FieldIdsOffset();
180 header.method_ids_size_ = collections.MethodIdsSize();
181 header.method_ids_off_ = collections.MethodIdsOffset();
182 header.class_defs_size_ = collections.ClassDefsSize();
183 header.class_defs_off_ = collections.ClassDefsOffset();
184 header.data_size_ = header_->DataSize();
185 header.data_off_ = header_->DataOffset();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800186
187 // Compact dex specific flags.
188 header.debug_info_offsets_pos_ = debug_info_offsets_pos_;
189 header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_;
190 header.debug_info_base_ = debug_info_base_;
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800191 header.feature_flags_ = 0u;
192 // In cases where apps are converted to cdex during install, maintain feature flags so that
193 // the verifier correctly verifies apps that aren't targetting default methods.
194 if (header_->SupportDefaultMethods()) {
195 header.feature_flags_ |= static_cast<uint32_t>(CompactDexFile::FeatureFlags::kDefaultMethods);
196 }
Mathieu Chartier3e0c5172017-11-12 12:58:40 -0800197 UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u));
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700198}
199
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800200size_t CompactDexWriter::GetHeaderSize() const {
201 return sizeof(CompactDexFile::Header);
202}
203
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800204void CompactDexWriter::WriteMemMap() {
205 // Starting offset is right after the header.
206 uint32_t offset = GetHeaderSize();
207
208 dex_ir::Collections& collection = header_->GetCollections();
209
210 // Based on: https://source.android.com/devices/tech/dalvik/dex-format
211 // Since the offsets may not be calculated already, the writing must be done in the correct order.
212 const uint32_t string_ids_offset = offset;
213 offset += WriteStringIds(offset, /*reserve_only*/ true);
214 offset += WriteTypeIds(offset);
215 const uint32_t proto_ids_offset = offset;
216 offset += WriteProtoIds(offset, /*reserve_only*/ true);
217 offset += WriteFieldIds(offset);
218 offset += WriteMethodIds(offset);
219 const uint32_t class_defs_offset = offset;
220 offset += WriteClassDefs(offset, /*reserve_only*/ true);
221 const uint32_t call_site_ids_offset = offset;
222 offset += WriteCallSiteIds(offset, /*reserve_only*/ true);
223 offset += WriteMethodHandles(offset);
224
225 uint32_t data_offset_ = 0u;
226 if (compute_offsets_) {
227 // Data section.
228 offset = RoundUp(offset, kDataSectionAlignment);
229 data_offset_ = offset;
230 }
231
232 // Write code item first to minimize the space required for encoded methods.
233 // For cdex, the code items don't depend on the debug info.
234 offset += WriteCodeItems(offset, /*reserve_only*/ false);
235
236 // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of
237 // the debug info offset table.
238 SortDebugInfosByMethodIndex();
239 offset += WriteDebugInfoItems(offset);
240
241 offset += WriteEncodedArrays(offset);
242 offset += WriteAnnotations(offset);
243 offset += WriteAnnotationSets(offset);
244 offset += WriteAnnotationSetRefs(offset);
245 offset += WriteAnnotationsDirectories(offset);
246 offset += WriteTypeLists(offset);
247 offset += WriteClassDatas(offset);
248 offset += WriteStringDatas(offset);
249
250 // Write delayed id sections that depend on data sections.
251 WriteStringIds(string_ids_offset, /*reserve_only*/ false);
252 WriteProtoIds(proto_ids_offset, /*reserve_only*/ false);
253 WriteClassDefs(class_defs_offset, /*reserve_only*/ false);
254 WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false);
255
256 // Write the map list.
257 if (compute_offsets_) {
258 offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList));
259 collection.SetMapListOffset(offset);
260 } else {
261 offset = collection.MapListOffset();
262 }
263 offset += GenerateAndWriteMapItems(offset);
264 offset = RoundUp(offset, kDataSectionAlignment);
265
266 // Map items are included in the data section.
267 if (compute_offsets_) {
268 header_->SetDataSize(offset - data_offset_);
269 if (header_->DataSize() != 0) {
270 // Offset must be zero when the size is zero.
271 header_->SetDataOffset(data_offset_);
272 } else {
273 header_->SetDataOffset(0u);
274 }
275 }
276
277 // Write link data if it exists.
278 const std::vector<uint8_t>& link_data = collection.LinkData();
279 if (link_data.size() > 0) {
280 CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
281 if (compute_offsets_) {
282 header_->SetLinkOffset(offset);
283 }
284 offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
285 }
286
287 // Write debug info offset table last to make dex file verifier happy.
288 offset += WriteDebugInfoOffsetTable(offset);
289
290 // Write header last.
291 if (compute_offsets_) {
292 header_->SetFileSize(offset);
293 }
294 WriteHeader();
295
296 if (dex_layout_->GetOptions().update_checksum_) {
297 header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset));
298 // Rewrite the header with the calculated checksum.
299 WriteHeader();
300 }
301}
302
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700303} // namespace art