blob: a7dad0762d6d5a9cf2989c38abf454e811a4bb2b [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 Chartier05f90d12018-02-07 13:47:17 -080019#include "android-base/stringprintf.h"
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080020#include "base/logging.h"
21#include "base/time_utils.h"
David Sehr9e734c72018-01-04 17:56:19 -080022#include "dex/compact_dex_file.h"
Mathieu Chartier5e3cfa22018-02-20 16:53:37 -080023#include "dex/compact_offset_table.h"
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080024#include "dexlayout.h"
Mathieu Chartierf95a75e2017-11-03 15:25:52 -070025
26namespace art {
27
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080028CompactDexWriter::CompactDexWriter(DexLayout* dex_layout)
Andreas Gampe9b031f72018-10-04 11:03:34 -070029 : DexWriter(dex_layout, /*compute_offsets=*/ true) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080030 CHECK(GetCompactDexLevel() != CompactDexLevel::kCompactDexLevelNone);
Mathieu Chartier66c9df12018-01-16 14:45:20 -080031}
32
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080033CompactDexLevel CompactDexWriter::GetCompactDexLevel() const {
34 return dex_layout_->GetOptions().compact_dex_level_;
35}
36
37CompactDexWriter::Container::Container(bool dedupe_code_items)
Mathieu Chartierb81ecad2018-01-23 22:08:26 -080038 : code_item_dedupe_(dedupe_code_items, &data_section_),
Andreas Gampe9b031f72018-10-04 11:03:34 -070039 data_item_dedupe_(/*enabled=*/ true, &data_section_) {}
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080040
41uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(Stream* stream) {
42 const uint32_t start_offset = stream->Tell();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080043 // Debug offsets for method indexes. 0 means no debug info.
David Sehr2b5a38f2018-06-14 15:13:04 -070044 std::vector<uint32_t> debug_info_offsets(header_->MethodIds().Size(), 0u);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080045
46 static constexpr InvokeType invoke_types[] = {
47 kDirect,
48 kVirtual
49 };
50
51 for (InvokeType invoke_type : invoke_types) {
David Sehr2b5a38f2018-06-14 15:13:04 -070052 for (auto& class_def : header_->ClassDefs()) {
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080053 // Skip classes that are not defined in this dex file.
54 dex_ir::ClassData* class_data = class_def->GetClassData();
55 if (class_data == nullptr) {
56 continue;
57 }
58 for (auto& method : *(invoke_type == InvokeType::kDirect
59 ? class_data->DirectMethods()
60 : class_data->VirtualMethods())) {
David Sehrd83437c2018-06-11 14:06:23 -070061 const dex_ir::MethodId* method_id = method.GetMethodId();
62 dex_ir::CodeItem* code_item = method.GetCodeItem();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080063 if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
64 const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset();
65 const uint32_t method_idx = method_id->GetIndex();
66 if (debug_info_offsets[method_idx] != 0u) {
67 CHECK_EQ(debug_info_offset, debug_info_offsets[method_idx]);
68 }
69 debug_info_offsets[method_idx] = debug_info_offset;
70 }
71 }
72 }
73 }
74
75 std::vector<uint8_t> data;
76 debug_info_base_ = 0u;
77 debug_info_offsets_table_offset_ = 0u;
Mathieu Chartier5e3cfa22018-02-20 16:53:37 -080078 CompactOffsetTable::Build(debug_info_offsets,
79 &data,
80 &debug_info_base_,
81 &debug_info_offsets_table_offset_);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080082 // Align the table and write it out.
Mathieu Chartier5e3cfa22018-02-20 16:53:37 -080083 stream->AlignTo(CompactOffsetTable::kAlignment);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080084 debug_info_offsets_pos_ = stream->Tell();
85 stream->Write(data.data(), data.size());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080086
87 // Verify that the whole table decodes as expected and measure average performance.
88 const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_;
89 if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
90 uint64_t start_time = NanoTime();
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -080091 stream->Begin();
Mathieu Chartier5e3cfa22018-02-20 16:53:37 -080092 CompactOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
93 debug_info_base_,
94 debug_info_offsets_table_offset_);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080095
96 for (size_t i = 0; i < debug_info_offsets.size(); ++i) {
Mathieu Chartier5e3cfa22018-02-20 16:53:37 -080097 CHECK_EQ(accessor.GetOffset(i), debug_info_offsets[i]);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -080098 }
99 uint64_t end_time = NanoTime();
100 VLOG(dex) << "Average lookup time (ns) for debug info offsets: "
101 << (end_time - start_time) / debug_info_offsets.size();
102 }
103
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800104 return stream->Tell() - start_offset;
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800105}
106
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800107CompactDexWriter::ScopedDataSectionItem::ScopedDataSectionItem(Stream* stream,
108 dex_ir::Item* item,
109 size_t alignment,
110 Deduper* deduper)
111 : stream_(stream),
112 item_(item),
113 alignment_(alignment),
114 deduper_(deduper),
115 start_offset_(stream->Tell()) {
116 stream_->AlignTo(alignment_);
117}
118
119CompactDexWriter::ScopedDataSectionItem::~ScopedDataSectionItem() {
120 // After having written, maybe dedupe the whole code item (excluding padding).
121 const uint32_t deduped_offset = deduper_->Dedupe(start_offset_,
122 stream_->Tell(),
123 item_->GetOffset());
Mathieu Chartier807b21b2018-01-29 05:18:24 -0800124 // If we deduped, only use the deduped offset if the alignment matches the required alignment.
125 // Otherwise, return without deduping.
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800126 if (deduped_offset != Deduper::kDidNotDedupe && IsAlignedParam(deduped_offset, alignment_)) {
Mathieu Chartier807b21b2018-01-29 05:18:24 -0800127 // Update the IR offset to the offset of the deduped item.
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800128 item_->SetOffset(deduped_offset);
Mathieu Chartier807b21b2018-01-29 05:18:24 -0800129 // Clear the written data for the item so that the stream write doesn't abort in the future.
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800130 stream_->Clear(start_offset_, stream_->Tell() - start_offset_);
Mathieu Chartier807b21b2018-01-29 05:18:24 -0800131 // Since we deduped, restore the offset to the original position.
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800132 stream_->Seek(start_offset_);
133 }
134}
135
136size_t CompactDexWriter::ScopedDataSectionItem::Written() const {
137 return stream_->Tell() - start_offset_;
138}
139
140void CompactDexWriter::WriteCodeItem(Stream* stream,
141 dex_ir::CodeItem* code_item,
142 bool reserve_only) {
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800143 DCHECK(code_item != nullptr);
Mathieu Chartier66c9df12018-01-16 14:45:20 -0800144 DCHECK(!reserve_only) << "Not supported because of deduping.";
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800145 ScopedDataSectionItem data_item(stream,
146 code_item,
147 CompactDexFile::CodeItem::kAlignment,
148 code_item_dedupe_);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800149
150 CompactDexFile::CodeItem disk_code_item;
Mathieu Chartier8740c662018-01-11 14:50:02 -0800151
152 uint16_t preheader_storage[CompactDexFile::CodeItem::kMaxPreHeaderSize] = {};
153 uint16_t* preheader_end = preheader_storage + CompactDexFile::CodeItem::kMaxPreHeaderSize;
154 const uint16_t* preheader = disk_code_item.Create(
155 code_item->RegistersSize(),
156 code_item->InsSize(),
157 code_item->OutsSize(),
158 code_item->TriesSize(),
159 code_item->InsnsSize(),
160 preheader_end);
161 const size_t preheader_bytes = (preheader_end - preheader) * sizeof(preheader[0]);
162
163 static constexpr size_t kPayloadInstructionRequiredAlignment = 4;
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800164 const uint32_t current_code_item_start = stream->Tell() + preheader_bytes;
Mathieu Chartiera27af082018-02-03 16:12:23 -0800165 if (!IsAlignedParam(current_code_item_start, kPayloadInstructionRequiredAlignment) ||
166 kIsDebugBuild) {
Mathieu Chartier8740c662018-01-11 14:50:02 -0800167 // If the preheader is going to make the code unaligned, consider adding 2 bytes of padding
168 // before if required.
Mathieu Chartiera27af082018-02-03 16:12:23 -0800169 IterationRange<DexInstructionIterator> instructions = code_item->Instructions();
170 SafeDexInstructionIterator it(instructions.begin(), instructions.end());
171 for (; !it.IsErrorState() && it < instructions.end(); ++it) {
172 // In case the instruction goes past the end of the code item, make sure to not process it.
173 if (std::next(it).IsErrorState()) {
174 break;
175 }
176 const Instruction::Code opcode = it->Opcode();
Mathieu Chartier8740c662018-01-11 14:50:02 -0800177 // Payload instructions possibly require special alignment for their data.
178 if (opcode == Instruction::FILL_ARRAY_DATA ||
179 opcode == Instruction::PACKED_SWITCH ||
180 opcode == Instruction::SPARSE_SWITCH) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800181 stream->Skip(
182 RoundUp(current_code_item_start, kPayloadInstructionRequiredAlignment) -
183 current_code_item_start);
Mathieu Chartier8740c662018-01-11 14:50:02 -0800184 break;
185 }
186 }
187 }
188
189 // Write preheader first.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800190 stream->Write(reinterpret_cast<const uint8_t*>(preheader), preheader_bytes);
Mathieu Chartier8740c662018-01-11 14:50:02 -0800191 // Registered offset is after the preheader.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800192 ProcessOffset(stream, code_item);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800193 // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
194 // item.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800195 stream->Write(&disk_code_item, OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_));
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800196 // Write the instructions.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800197 stream->Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t));
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800198 // Write the post instruction data.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800199 WriteCodeItemPostInstructionData(stream, code_item, reserve_only);
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800200}
Mathieu Chartier66c9df12018-01-16 14:45:20 -0800201
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800202void CompactDexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info) {
203 ScopedDataSectionItem data_item(stream,
204 debug_info,
205 SectionAlignment(DexFile::kDexTypeDebugInfoItem),
206 data_item_dedupe_);
207 ProcessOffset(stream, debug_info);
208 stream->Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800209}
210
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800211
212CompactDexWriter::Deduper::Deduper(bool enabled, DexContainer::Section* section)
213 : enabled_(enabled),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700214 dedupe_map_(/*__n=*/ 32,
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800215 HashedMemoryRange::HashEqual(section),
216 HashedMemoryRange::HashEqual(section)) {}
217
218uint32_t CompactDexWriter::Deduper::Dedupe(uint32_t data_start,
219 uint32_t data_end,
220 uint32_t item_offset) {
221 if (!enabled_) {
222 return kDidNotDedupe;
223 }
Mathieu Chartier66c9df12018-01-16 14:45:20 -0800224 HashedMemoryRange range {data_start, data_end - data_start};
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800225 auto existing = dedupe_map_.emplace(range, item_offset);
Mathieu Chartier66c9df12018-01-16 14:45:20 -0800226 if (!existing.second) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800227 // Failed to insert means we deduped, return the existing item offset.
Mathieu Chartier66c9df12018-01-16 14:45:20 -0800228 return existing.first->second;
229 }
230 return kDidNotDedupe;
231}
232
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800233void CompactDexWriter::SortDebugInfosByMethodIndex() {
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800234 static constexpr InvokeType invoke_types[] = {
235 kDirect,
236 kVirtual
237 };
238 std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map;
239 for (InvokeType invoke_type : invoke_types) {
David Sehr2b5a38f2018-06-14 15:13:04 -0700240 for (auto& class_def : header_->ClassDefs()) {
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800241 // Skip classes that are not defined in this dex file.
242 dex_ir::ClassData* class_data = class_def->GetClassData();
243 if (class_data == nullptr) {
244 continue;
245 }
246 for (auto& method : *(invoke_type == InvokeType::kDirect
247 ? class_data->DirectMethods()
248 : class_data->VirtualMethods())) {
David Sehrd83437c2018-06-11 14:06:23 -0700249 const dex_ir::MethodId* method_id = method.GetMethodId();
250 dex_ir::CodeItem* code_item = method.GetCodeItem();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800251 if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
252 const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
253 method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
254 }
255 }
256 }
257 }
David Sehr2b5a38f2018-06-14 15:13:04 -0700258 std::sort(header_->DebugInfoItems().begin(),
259 header_->DebugInfoItems().end(),
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800260 [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a,
261 const std::unique_ptr<dex_ir::DebugInfoItem>& b) {
262 auto it_a = method_idx_map.find(a.get());
263 auto it_b = method_idx_map.find(b.get());
264 uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u;
265 uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u;
266 return idx_a < idx_b;
267 });
268}
269
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800270void CompactDexWriter::WriteHeader(Stream* stream) {
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700271 CompactDexFile::Header header;
272 CompactDexFile::WriteMagic(&header.magic_[0]);
273 CompactDexFile::WriteCurrentVersion(&header.magic_[0]);
274 header.checksum_ = header_->Checksum();
275 std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_);
276 header.file_size_ = header_->FileSize();
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800277 // Since we are not necessarily outputting the same format as the input, avoid using the stored
278 // header size.
279 header.header_size_ = GetHeaderSize();
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700280 header.endian_tag_ = header_->EndianTag();
281 header.link_size_ = header_->LinkSize();
282 header.link_off_ = header_->LinkOffset();
David Sehr2b5a38f2018-06-14 15:13:04 -0700283 header.map_off_ = header_->MapListOffset();
284 header.string_ids_size_ = header_->StringIds().Size();
285 header.string_ids_off_ = header_->StringIds().GetOffset();
286 header.type_ids_size_ = header_->TypeIds().Size();
287 header.type_ids_off_ = header_->TypeIds().GetOffset();
288 header.proto_ids_size_ = header_->ProtoIds().Size();
289 header.proto_ids_off_ = header_->ProtoIds().GetOffset();
290 header.field_ids_size_ = header_->FieldIds().Size();
291 header.field_ids_off_ = header_->FieldIds().GetOffset();
292 header.method_ids_size_ = header_->MethodIds().Size();
293 header.method_ids_off_ = header_->MethodIds().GetOffset();
294 header.class_defs_size_ = header_->ClassDefs().Size();
295 header.class_defs_off_ = header_->ClassDefs().GetOffset();
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700296 header.data_size_ = header_->DataSize();
297 header.data_off_ = header_->DataOffset();
Mathieu Chartierc17b7d82018-03-14 14:00:04 -0700298 header.owned_data_begin_ = owned_data_begin_;
299 header.owned_data_end_ = owned_data_end_;
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800300
301 // Compact dex specific flags.
302 header.debug_info_offsets_pos_ = debug_info_offsets_pos_;
303 header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_;
304 header.debug_info_base_ = debug_info_base_;
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800305 header.feature_flags_ = 0u;
306 // In cases where apps are converted to cdex during install, maintain feature flags so that
307 // the verifier correctly verifies apps that aren't targetting default methods.
308 if (header_->SupportDefaultMethods()) {
309 header.feature_flags_ |= static_cast<uint32_t>(CompactDexFile::FeatureFlags::kDefaultMethods);
310 }
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800311 stream->Seek(0);
312 stream->Overwrite(reinterpret_cast<uint8_t*>(&header), sizeof(header));
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700313}
314
Mathieu Chartierf6e31472017-12-28 13:32:08 -0800315size_t CompactDexWriter::GetHeaderSize() const {
316 return sizeof(CompactDexFile::Header);
317}
318
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800319void CompactDexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) {
320 ScopedDataSectionItem data_item(stream,
321 string_data,
322 SectionAlignment(DexFile::kDexTypeStringDataItem),
323 data_item_dedupe_);
324 ProcessOffset(stream, string_data);
325 stream->WriteUleb128(CountModifiedUtf8Chars(string_data->Data()));
326 stream->Write(string_data->Data(), strlen(string_data->Data()));
327 // Skip null terminator (already zeroed out, no need to write).
328 stream->Skip(1);
329}
330
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800331bool CompactDexWriter::CanGenerateCompactDex(std::string* error_msg) {
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800332 static constexpr InvokeType invoke_types[] = {
333 kDirect,
334 kVirtual
335 };
David Sehr2b5a38f2018-06-14 15:13:04 -0700336 std::vector<bool> saw_method_id(header_->MethodIds().Size(), false);
337 std::vector<dex_ir::CodeItem*> method_id_code_item(header_->MethodIds().Size(), nullptr);
338 std::vector<dex_ir::DebugInfoItem*> method_id_debug_info(header_->MethodIds().Size(), nullptr);
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800339 for (InvokeType invoke_type : invoke_types) {
David Sehr2b5a38f2018-06-14 15:13:04 -0700340 for (auto& class_def : header_->ClassDefs()) {
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800341 // Skip classes that are not defined in this dex file.
342 dex_ir::ClassData* class_data = class_def->GetClassData();
343 if (class_data == nullptr) {
344 continue;
345 }
346 for (auto& method : *(invoke_type == InvokeType::kDirect
347 ? class_data->DirectMethods()
348 : class_data->VirtualMethods())) {
David Sehrd83437c2018-06-11 14:06:23 -0700349 const uint32_t idx = method.GetMethodId()->GetIndex();
350 dex_ir::CodeItem* code_item = method.GetCodeItem();
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800351 dex_ir:: DebugInfoItem* debug_info_item = nullptr;
352 if (code_item != nullptr) {
353 debug_info_item = code_item->DebugInfo();
354 }
355 if (saw_method_id[idx]) {
356 if (method_id_code_item[idx] != code_item) {
357 *error_msg = android::base::StringPrintf("Conflicting code item for method id %u",
358 idx);
359 // Conflicting info, abort generation.
360 return false;
361 }
362 if (method_id_debug_info[idx] != debug_info_item) {
363 *error_msg = android::base::StringPrintf("Conflicting debug info for method id %u",
364 idx);
365 // Conflicting info, abort generation.
366 return false;
367 }
368 }
369 method_id_code_item[idx] = code_item;
370 method_id_debug_info[idx] = debug_info_item;
371 saw_method_id[idx] = true;
372 }
373 }
374 }
375 return true;
376}
377
378bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) {
379 DCHECK(error_msg != nullptr);
Mathieu Chartier9b302bf2018-01-25 13:08:08 -0800380 CHECK(compute_offsets_);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800381 CHECK(output->IsCompactDexContainer());
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800382
383 if (!CanGenerateCompactDex(error_msg)) {
384 return false;
385 }
386
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800387 Container* const container = down_cast<Container*>(output);
388 // For now, use the same stream for both data and metadata.
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800389 Stream temp_main_stream(output->GetMainSection());
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800390 CHECK_EQ(output->GetMainSection()->Size(), 0u);
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800391 Stream temp_data_stream(output->GetDataSection());
392 Stream* main_stream = &temp_main_stream;
393 Stream* data_stream = &temp_data_stream;
394
395 // We want offset 0 to be reserved for null, seek to the data section alignment or the end of the
396 // section.
397 data_stream->Seek(std::max(
398 static_cast<uint32_t>(output->GetDataSection()->Size()),
399 kDataSectionAlignment));
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800400 code_item_dedupe_ = &container->code_item_dedupe_;
Mathieu Chartierb81ecad2018-01-23 22:08:26 -0800401 data_item_dedupe_ = &container->data_item_dedupe_;
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800402
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800403 // Starting offset is right after the header.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800404 main_stream->Seek(GetHeaderSize());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800405
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800406 // Based on: https://source.android.com/devices/tech/dalvik/dex-format
407 // Since the offsets may not be calculated already, the writing must be done in the correct order.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800408 const uint32_t string_ids_offset = main_stream->Tell();
Andreas Gampe9b031f72018-10-04 11:03:34 -0700409 WriteStringIds(main_stream, /*reserve_only=*/ true);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800410 WriteTypeIds(main_stream);
411 const uint32_t proto_ids_offset = main_stream->Tell();
Andreas Gampe9b031f72018-10-04 11:03:34 -0700412 WriteProtoIds(main_stream, /*reserve_only=*/ true);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800413 WriteFieldIds(main_stream);
414 WriteMethodIds(main_stream);
415 const uint32_t class_defs_offset = main_stream->Tell();
Andreas Gampe9b031f72018-10-04 11:03:34 -0700416 WriteClassDefs(main_stream, /*reserve_only=*/ true);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800417 const uint32_t call_site_ids_offset = main_stream->Tell();
Andreas Gampe9b031f72018-10-04 11:03:34 -0700418 WriteCallSiteIds(main_stream, /*reserve_only=*/ true);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800419 WriteMethodHandles(main_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800420
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800421 if (compute_offsets_) {
422 // Data section.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800423 data_stream->AlignTo(kDataSectionAlignment);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800424 }
Mathieu Chartierc17b7d82018-03-14 14:00:04 -0700425 owned_data_begin_ = data_stream->Tell();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800426
427 // Write code item first to minimize the space required for encoded methods.
428 // For cdex, the code items don't depend on the debug info.
Andreas Gampe9b031f72018-10-04 11:03:34 -0700429 WriteCodeItems(data_stream, /*reserve_only=*/ false);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800430
431 // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of
432 // the debug info offset table.
433 SortDebugInfosByMethodIndex();
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800434 WriteDebugInfoItems(data_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800435
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800436 WriteEncodedArrays(data_stream);
437 WriteAnnotations(data_stream);
438 WriteAnnotationSets(data_stream);
439 WriteAnnotationSetRefs(data_stream);
440 WriteAnnotationsDirectories(data_stream);
441 WriteTypeLists(data_stream);
442 WriteClassDatas(data_stream);
443 WriteStringDatas(data_stream);
David Brazdil20c765f2018-10-27 21:45:15 +0000444 WriteHiddenapiClassData(data_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800445
446 // Write delayed id sections that depend on data sections.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800447 {
448 Stream::ScopedSeek seek(main_stream, string_ids_offset);
Andreas Gampe9b031f72018-10-04 11:03:34 -0700449 WriteStringIds(main_stream, /*reserve_only=*/ false);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800450 }
451 {
452 Stream::ScopedSeek seek(main_stream, proto_ids_offset);
Andreas Gampe9b031f72018-10-04 11:03:34 -0700453 WriteProtoIds(main_stream, /*reserve_only=*/ false);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800454 }
455 {
456 Stream::ScopedSeek seek(main_stream, class_defs_offset);
Andreas Gampe9b031f72018-10-04 11:03:34 -0700457 WriteClassDefs(main_stream, /*reserve_only=*/ false);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800458 }
459 {
460 Stream::ScopedSeek seek(main_stream, call_site_ids_offset);
Andreas Gampe9b031f72018-10-04 11:03:34 -0700461 WriteCallSiteIds(main_stream, /*reserve_only=*/ false);
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800462 }
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800463
464 // Write the map list.
465 if (compute_offsets_) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800466 data_stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList));
David Sehr2b5a38f2018-06-14 15:13:04 -0700467 header_->SetMapListOffset(data_stream->Tell());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800468 } else {
David Sehr2b5a38f2018-06-14 15:13:04 -0700469 data_stream->Seek(header_->MapListOffset());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800470 }
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800471
472 // Map items are included in the data section.
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800473 GenerateAndWriteMapItems(data_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800474
475 // Write link data if it exists.
David Sehr2b5a38f2018-06-14 15:13:04 -0700476 const std::vector<uint8_t>& link_data = header_->LinkData();
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800477 if (link_data.size() > 0) {
478 CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
479 if (compute_offsets_) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800480 header_->SetLinkOffset(data_stream->Tell());
481 } else {
482 data_stream->Seek(header_->LinkOffset());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800483 }
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800484 data_stream->Write(&link_data[0], link_data.size());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800485 }
486
487 // Write debug info offset table last to make dex file verifier happy.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800488 WriteDebugInfoOffsetTable(data_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800489
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800490 data_stream->AlignTo(kDataSectionAlignment);
Mathieu Chartierc17b7d82018-03-14 14:00:04 -0700491 owned_data_end_ = data_stream->Tell();
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800492 if (compute_offsets_) {
493 header_->SetDataSize(data_stream->Tell());
494 if (header_->DataSize() != 0) {
495 // Offset must be zero when the size is zero.
496 main_stream->AlignTo(kDataSectionAlignment);
497 // For now, default to saying the data is right after the main stream.
498 header_->SetDataOffset(main_stream->Tell());
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800499 } else {
500 header_->SetDataOffset(0u);
501 }
502 }
503
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800504 // Write header last.
505 if (compute_offsets_) {
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800506 header_->SetFileSize(main_stream->Tell());
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800507 }
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800508 WriteHeader(main_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800509
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800510 // Trim sections to make sure they are sized properly.
511 output->GetMainSection()->Resize(header_->FileSize());
512 output->GetDataSection()->Resize(data_stream->Tell());
513
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800514 if (dex_layout_->GetOptions().update_checksum_) {
Mathieu Chartierc3a22aa2018-01-19 18:58:34 -0800515 // Compute the cdex section (also covers the used part of the data section).
516 header_->SetChecksum(CompactDexFile::CalculateChecksum(output->GetMainSection()->Begin(),
517 output->GetMainSection()->Size(),
518 output->GetDataSection()->Begin(),
519 output->GetDataSection()->Size()));
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800520 // Rewrite the header with the calculated checksum.
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800521 WriteHeader(main_stream);
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800522 }
Mathieu Chartier279e3a32018-01-24 18:17:55 -0800523
524 // Clear the dedupe to prevent interdex code item deduping. This does not currently work well with
525 // dex2oat's class unloading. The issue is that verification encounters quickened opcodes after
526 // the first dex gets unloaded.
527 code_item_dedupe_->Clear();
Mathieu Chartier05f90d12018-02-07 13:47:17 -0800528
529 return true;
Mathieu Chartiere6b6ff82018-01-19 18:58:34 -0800530}
531
532std::unique_ptr<DexContainer> CompactDexWriter::CreateDexContainer() const {
533 return std::unique_ptr<DexContainer>(
534 new CompactDexWriter::Container(dex_layout_->GetOptions().dedupe_code_items_));
Mathieu Chartier8892c6b2018-01-09 15:10:17 -0800535}
536
Mathieu Chartierf95a75e2017-11-03 15:25:52 -0700537} // namespace art