| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "dex_writer.h" |
| |
| #include <stdint.h> |
| |
| #include <vector> |
| |
| #include "compact_dex_writer.h" |
| #include "dex/compact_dex_file.h" |
| #include "dex/dex_file_layout.h" |
| #include "dex/dex_file_types.h" |
| #include "dex/standard_dex_file.h" |
| #include "dex/utf.h" |
| #include "dexlayout.h" |
| |
| namespace art { |
| |
| constexpr uint32_t DexWriter::kDataSectionAlignment; |
| |
| static size_t EncodeIntValue(int32_t value, uint8_t* buffer) { |
| size_t length = 0; |
| if (value >= 0) { |
| while (value > 0x7f) { |
| buffer[length++] = static_cast<uint8_t>(value); |
| value >>= 8; |
| } |
| } else { |
| while (value < -0x80) { |
| buffer[length++] = static_cast<uint8_t>(value); |
| value >>= 8; |
| } |
| } |
| buffer[length++] = static_cast<uint8_t>(value); |
| return length; |
| } |
| |
| static size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) { |
| size_t length = 0; |
| do { |
| buffer[length++] = static_cast<uint8_t>(value); |
| value >>= 8; |
| } while (value != 0); |
| return length; |
| } |
| |
| static size_t EncodeLongValue(int64_t value, uint8_t* buffer) { |
| size_t length = 0; |
| if (value >= 0) { |
| while (value > 0x7f) { |
| buffer[length++] = static_cast<uint8_t>(value); |
| value >>= 8; |
| } |
| } else { |
| while (value < -0x80) { |
| buffer[length++] = static_cast<uint8_t>(value); |
| value >>= 8; |
| } |
| } |
| buffer[length++] = static_cast<uint8_t>(value); |
| return length; |
| } |
| |
| union FloatUnion { |
| float f_; |
| uint32_t i_; |
| }; |
| |
| static size_t EncodeFloatValue(float value, uint8_t* buffer) { |
| FloatUnion float_union; |
| float_union.f_ = value; |
| uint32_t int_value = float_union.i_; |
| size_t index = 3; |
| do { |
| buffer[index--] = int_value >> 24; |
| int_value <<= 8; |
| } while (int_value != 0); |
| return 3 - index; |
| } |
| |
| union DoubleUnion { |
| double d_; |
| uint64_t l_; |
| }; |
| |
| static size_t EncodeDoubleValue(double value, uint8_t* buffer) { |
| DoubleUnion double_union; |
| double_union.d_ = value; |
| uint64_t long_value = double_union.l_; |
| size_t index = 7; |
| do { |
| buffer[index--] = long_value >> 56; |
| long_value <<= 8; |
| } while (long_value != 0); |
| return 7 - index; |
| } |
| |
| DexWriter::DexWriter(DexLayout* dex_layout, bool compute_offsets) |
| : header_(dex_layout->GetHeader()), |
| dex_layout_(dex_layout), |
| compute_offsets_(compute_offsets) {} |
| |
| void DexWriter::WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value) { |
| size_t start = 0; |
| size_t length; |
| uint8_t buffer[8]; |
| int8_t type = encoded_value->Type(); |
| switch (type) { |
| case DexFile::kDexAnnotationByte: |
| length = EncodeIntValue(encoded_value->GetByte(), buffer); |
| break; |
| case DexFile::kDexAnnotationShort: |
| length = EncodeIntValue(encoded_value->GetShort(), buffer); |
| break; |
| case DexFile::kDexAnnotationChar: |
| length = EncodeUIntValue(encoded_value->GetChar(), buffer); |
| break; |
| case DexFile::kDexAnnotationInt: |
| length = EncodeIntValue(encoded_value->GetInt(), buffer); |
| break; |
| case DexFile::kDexAnnotationLong: |
| length = EncodeLongValue(encoded_value->GetLong(), buffer); |
| break; |
| case DexFile::kDexAnnotationFloat: |
| length = EncodeFloatValue(encoded_value->GetFloat(), buffer); |
| start = 4 - length; |
| break; |
| case DexFile::kDexAnnotationDouble: |
| length = EncodeDoubleValue(encoded_value->GetDouble(), buffer); |
| start = 8 - length; |
| break; |
| case DexFile::kDexAnnotationMethodType: |
| length = EncodeUIntValue(encoded_value->GetProtoId()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationMethodHandle: |
| length = EncodeUIntValue(encoded_value->GetMethodHandle()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationString: |
| length = EncodeUIntValue(encoded_value->GetStringId()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationType: |
| length = EncodeUIntValue(encoded_value->GetTypeId()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationField: |
| case DexFile::kDexAnnotationEnum: |
| length = EncodeUIntValue(encoded_value->GetFieldId()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationMethod: |
| length = EncodeUIntValue(encoded_value->GetMethodId()->GetIndex(), buffer); |
| break; |
| case DexFile::kDexAnnotationArray: |
| WriteEncodedValueHeader(stream, type, 0); |
| WriteEncodedArray(stream, encoded_value->GetEncodedArray()->GetEncodedValues()); |
| return; |
| case DexFile::kDexAnnotationAnnotation: |
| WriteEncodedValueHeader(stream, type, 0); |
| WriteEncodedAnnotation(stream, encoded_value->GetEncodedAnnotation()); |
| return; |
| case DexFile::kDexAnnotationNull: |
| WriteEncodedValueHeader(stream, type, 0); |
| return; |
| case DexFile::kDexAnnotationBoolean: |
| WriteEncodedValueHeader(stream, type, encoded_value->GetBoolean() ? 1 : 0); |
| return; |
| default: |
| return; |
| } |
| WriteEncodedValueHeader(stream, type, length - 1); |
| stream->Write(buffer + start, length); |
| } |
| |
| void DexWriter::WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg) { |
| uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) }; |
| stream->Write(buffer, sizeof(uint8_t)); |
| } |
| |
| void DexWriter::WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values) { |
| stream->WriteUleb128(values->size()); |
| for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) { |
| WriteEncodedValue(stream, value.get()); |
| } |
| } |
| |
| void DexWriter::WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation) { |
| stream->WriteUleb128(annotation->GetType()->GetIndex()); |
| stream->WriteUleb128(annotation->GetAnnotationElements()->size()); |
| for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element : |
| *annotation->GetAnnotationElements()) { |
| stream->WriteUleb128(annotation_element->GetName()->GetIndex()); |
| WriteEncodedValue(stream, annotation_element->GetValue()); |
| } |
| } |
| |
| void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) { |
| uint32_t prev_index = 0; |
| for (auto& field : *fields) { |
| uint32_t index = field.GetFieldId()->GetIndex(); |
| stream->WriteUleb128(index - prev_index); |
| stream->WriteUleb128(field.GetAccessFlags()); |
| prev_index = index; |
| } |
| } |
| |
| void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) { |
| uint32_t prev_index = 0; |
| for (auto& method : *methods) { |
| uint32_t index = method.GetMethodId()->GetIndex(); |
| uint32_t code_off = method.GetCodeItem() == nullptr ? 0 : method.GetCodeItem()->GetOffset(); |
| stream->WriteUleb128(index - prev_index); |
| stream->WriteUleb128(method.GetAccessFlags()); |
| stream->WriteUleb128(code_off); |
| prev_index = index; |
| } |
| } |
| |
| // TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding |
| // function that takes a CollectionVector<T> and uses overloading. |
| void DexWriter::WriteStringIds(Stream* stream, bool reserve_only) { |
| const uint32_t start = stream->Tell(); |
| for (auto& string_id : header_->StringIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringIdItem)); |
| if (reserve_only) { |
| stream->Skip(string_id->GetSize()); |
| } else { |
| uint32_t string_data_off = string_id->DataItem()->GetOffset(); |
| stream->Write(&string_data_off, string_id->GetSize()); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->StringIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteStringData(Stream* stream, dex_ir::StringData* string_data) { |
| ProcessOffset(stream, string_data); |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeStringDataItem)); |
| stream->WriteUleb128(CountModifiedUtf8Chars(string_data->Data())); |
| stream->Write(string_data->Data(), strlen(string_data->Data())); |
| // Skip null terminator (already zeroed out, no need to write). |
| stream->Skip(1); |
| } |
| |
| void DexWriter::WriteStringDatas(Stream* stream) { |
| const uint32_t start = stream->Tell(); |
| for (auto& string_data : header_->StringDatas()) { |
| WriteStringData(stream, string_data.get()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->StringDatas().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteTypeIds(Stream* stream) { |
| uint32_t descriptor_idx[1]; |
| const uint32_t start = stream->Tell(); |
| for (auto& type_id : header_->TypeIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeIdItem)); |
| ProcessOffset(stream, type_id.get()); |
| descriptor_idx[0] = type_id->GetStringId()->GetIndex(); |
| stream->Write(descriptor_idx, type_id->GetSize()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->TypeIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteTypeLists(Stream* stream) { |
| uint32_t size[1]; |
| uint16_t list[1]; |
| const uint32_t start = stream->Tell(); |
| for (auto& type_list : header_->TypeLists()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeTypeList)); |
| size[0] = type_list->GetTypeList()->size(); |
| ProcessOffset(stream, type_list.get()); |
| stream->Write(size, sizeof(uint32_t)); |
| for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { |
| list[0] = type_id->GetIndex(); |
| stream->Write(list, sizeof(uint16_t)); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->TypeLists().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteProtoIds(Stream* stream, bool reserve_only) { |
| uint32_t buffer[3]; |
| const uint32_t start = stream->Tell(); |
| for (auto& proto_id : header_->ProtoIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeProtoIdItem)); |
| ProcessOffset(stream, proto_id.get()); |
| if (reserve_only) { |
| stream->Skip(proto_id->GetSize()); |
| } else { |
| buffer[0] = proto_id->Shorty()->GetIndex(); |
| buffer[1] = proto_id->ReturnType()->GetIndex(); |
| buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); |
| stream->Write(buffer, proto_id->GetSize()); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->ProtoIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteFieldIds(Stream* stream) { |
| uint16_t buffer[4]; |
| const uint32_t start = stream->Tell(); |
| for (auto& field_id : header_->FieldIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeFieldIdItem)); |
| ProcessOffset(stream, field_id.get()); |
| buffer[0] = field_id->Class()->GetIndex(); |
| buffer[1] = field_id->Type()->GetIndex(); |
| buffer[2] = field_id->Name()->GetIndex(); |
| buffer[3] = field_id->Name()->GetIndex() >> 16; |
| stream->Write(buffer, field_id->GetSize()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->FieldIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteMethodIds(Stream* stream) { |
| uint16_t buffer[4]; |
| const uint32_t start = stream->Tell(); |
| for (auto& method_id : header_->MethodIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodIdItem)); |
| ProcessOffset(stream, method_id.get()); |
| buffer[0] = method_id->Class()->GetIndex(); |
| buffer[1] = method_id->Proto()->GetIndex(); |
| buffer[2] = method_id->Name()->GetIndex(); |
| buffer[3] = method_id->Name()->GetIndex() >> 16; |
| stream->Write(buffer, method_id->GetSize()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->MethodIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteEncodedArrays(Stream* stream) { |
| const uint32_t start = stream->Tell(); |
| for (auto& encoded_array : header_->EncodedArrayItems()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeEncodedArrayItem)); |
| ProcessOffset(stream, encoded_array.get()); |
| WriteEncodedArray(stream, encoded_array->GetEncodedValues()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->EncodedArrayItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteAnnotations(Stream* stream) { |
| uint8_t visibility[1]; |
| const uint32_t start = stream->Tell(); |
| for (auto& annotation : header_->AnnotationItems()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationItem)); |
| visibility[0] = annotation->GetVisibility(); |
| ProcessOffset(stream, annotation.get()); |
| stream->Write(visibility, sizeof(uint8_t)); |
| WriteEncodedAnnotation(stream, annotation->GetAnnotation()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->AnnotationItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteAnnotationSets(Stream* stream) { |
| uint32_t size[1]; |
| uint32_t annotation_off[1]; |
| const uint32_t start = stream->Tell(); |
| for (auto& annotation_set : header_->AnnotationSetItems()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetItem)); |
| size[0] = annotation_set->GetItems()->size(); |
| ProcessOffset(stream, annotation_set.get()); |
| stream->Write(size, sizeof(uint32_t)); |
| for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) { |
| annotation_off[0] = annotation->GetOffset(); |
| stream->Write(annotation_off, sizeof(uint32_t)); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->AnnotationSetItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteAnnotationSetRefs(Stream* stream) { |
| uint32_t size[1]; |
| uint32_t annotations_off[1]; |
| const uint32_t start = stream->Tell(); |
| for (auto& annotation_set_ref : header_->AnnotationSetRefLists()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationSetRefList)); |
| size[0] = annotation_set_ref->GetItems()->size(); |
| ProcessOffset(stream, annotation_set_ref.get()); |
| stream->Write(size, sizeof(uint32_t)); |
| for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) { |
| annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset(); |
| stream->Write(annotations_off, sizeof(uint32_t)); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->AnnotationSetRefLists().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteAnnotationsDirectories(Stream* stream) { |
| uint32_t directory_buffer[4]; |
| uint32_t annotation_buffer[2]; |
| const uint32_t start = stream->Tell(); |
| for (auto& annotations_directory : header_->AnnotationsDirectoryItems()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem)); |
| ProcessOffset(stream, annotations_directory.get()); |
| directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : |
| annotations_directory->GetClassAnnotation()->GetOffset(); |
| directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 : |
| annotations_directory->GetFieldAnnotations()->size(); |
| directory_buffer[2] = annotations_directory->GetMethodAnnotations() == nullptr ? 0 : |
| annotations_directory->GetMethodAnnotations()->size(); |
| directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 : |
| annotations_directory->GetParameterAnnotations()->size(); |
| stream->Write(directory_buffer, 4 * sizeof(uint32_t)); |
| if (annotations_directory->GetFieldAnnotations() != nullptr) { |
| for (std::unique_ptr<dex_ir::FieldAnnotation>& field : |
| *annotations_directory->GetFieldAnnotations()) { |
| annotation_buffer[0] = field->GetFieldId()->GetIndex(); |
| annotation_buffer[1] = field->GetAnnotationSetItem()->GetOffset(); |
| stream->Write(annotation_buffer, 2 * sizeof(uint32_t)); |
| } |
| } |
| if (annotations_directory->GetMethodAnnotations() != nullptr) { |
| for (std::unique_ptr<dex_ir::MethodAnnotation>& method : |
| *annotations_directory->GetMethodAnnotations()) { |
| annotation_buffer[0] = method->GetMethodId()->GetIndex(); |
| annotation_buffer[1] = method->GetAnnotationSetItem()->GetOffset(); |
| stream->Write(annotation_buffer, 2 * sizeof(uint32_t)); |
| } |
| } |
| if (annotations_directory->GetParameterAnnotations() != nullptr) { |
| for (std::unique_ptr<dex_ir::ParameterAnnotation>& parameter : |
| *annotations_directory->GetParameterAnnotations()) { |
| annotation_buffer[0] = parameter->GetMethodId()->GetIndex(); |
| annotation_buffer[1] = parameter->GetAnnotations()->GetOffset(); |
| stream->Write(annotation_buffer, 2 * sizeof(uint32_t)); |
| } |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->AnnotationsDirectoryItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteHiddenapiClassData(Stream* stream) { |
| if (header_->HiddenapiClassDatas().Empty()) { |
| return; |
| } |
| DCHECK_EQ(header_->HiddenapiClassDatas().Size(), header_->ClassDefs().Size()); |
| |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeHiddenapiClassData)); |
| const uint32_t start = stream->Tell(); |
| |
| // Compute offsets for each class def and write the header. |
| // data_header[0]: total size of the section |
| // data_header[i + 1]: offset of class def[i] from the beginning of the section, |
| // or zero if no data |
| std::vector<uint32_t> data_header(header_->ClassDefs().Size() + 1, 0); |
| data_header[0] = sizeof(uint32_t) * (header_->ClassDefs().Size() + 1); |
| for (uint32_t i = 0; i < header_->ClassDefs().Size(); ++i) { |
| uint32_t item_size = header_->HiddenapiClassDatas()[i]->ItemSize(); |
| data_header[i + 1] = item_size == 0u ? 0 : data_header[0]; |
| data_header[0] += item_size; |
| } |
| stream->Write(data_header.data(), sizeof(uint32_t) * data_header.size()); |
| |
| // Write class data streams. |
| for (uint32_t i = 0; i < header_->ClassDefs().Size(); ++i) { |
| dex_ir::ClassDef* class_def = header_->ClassDefs()[i]; |
| const auto& item = header_->HiddenapiClassDatas()[i]; |
| DCHECK(item->GetClassDef() == class_def); |
| |
| if (data_header[i + 1] != 0u) { |
| dex_ir::ClassData* class_data = class_def->GetClassData(); |
| DCHECK(class_data != nullptr); |
| DCHECK_EQ(data_header[i + 1], stream->Tell() - start); |
| for (const dex_ir::FieldItem& field : *class_data->StaticFields()) { |
| stream->WriteUleb128(item->GetFlags(&field)); |
| } |
| for (const dex_ir::FieldItem& field : *class_data->InstanceFields()) { |
| stream->WriteUleb128(item->GetFlags(&field)); |
| } |
| for (const dex_ir::MethodItem& method : *class_data->DirectMethods()) { |
| stream->WriteUleb128(item->GetFlags(&method)); |
| } |
| for (const dex_ir::MethodItem& method : *class_data->VirtualMethods()) { |
| stream->WriteUleb128(item->GetFlags(&method)); |
| } |
| } |
| } |
| DCHECK_EQ(stream->Tell() - start, data_header[0]); |
| |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->HiddenapiClassDatas().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeDebugInfoItem)); |
| ProcessOffset(stream, debug_info); |
| stream->Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize()); |
| } |
| |
| void DexWriter::WriteDebugInfoItems(Stream* stream) { |
| const uint32_t start = stream->Tell(); |
| for (auto& debug_info : header_->DebugInfoItems()) { |
| WriteDebugInfoItem(stream, debug_info.get()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->DebugInfoItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteCodeItemPostInstructionData(Stream* stream, |
| dex_ir::CodeItem* code_item, |
| bool reserve_only) { |
| if (code_item->TriesSize() != 0) { |
| stream->AlignTo(DexFile::TryItem::kAlignment); |
| // Write try items. |
| for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { |
| DexFile::TryItem disk_try_item; |
| if (!reserve_only) { |
| disk_try_item.start_addr_ = try_item->StartAddr(); |
| disk_try_item.insn_count_ = try_item->InsnCount(); |
| disk_try_item.handler_off_ = try_item->GetHandlers()->GetListOffset(); |
| } |
| stream->Write(&disk_try_item, sizeof(disk_try_item)); |
| } |
| // Leave offset pointing to the end of the try items. |
| const size_t offset = stream->Tell(); |
| size_t max_offset = offset + stream->WriteUleb128(code_item->Handlers()->size()); |
| for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { |
| stream->Seek(offset + handlers->GetListOffset()); |
| uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : |
| handlers->GetHandlers()->size(); |
| stream->WriteSleb128(size); |
| for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { |
| if (handler->GetTypeId() != nullptr) { |
| stream->WriteUleb128(handler->GetTypeId()->GetIndex()); |
| } |
| stream->WriteUleb128(handler->GetAddress()); |
| } |
| // TODO: Clean this up to write the handlers in address order. |
| max_offset = std::max(max_offset, stream->Tell()); |
| } |
| stream->Seek(max_offset); |
| } |
| } |
| |
| void DexWriter::WriteCodeItem(Stream* stream, |
| dex_ir::CodeItem* code_item, |
| bool reserve_only) { |
| DCHECK(code_item != nullptr); |
| const uint32_t start_offset = stream->Tell(); |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeCodeItem)); |
| ProcessOffset(stream, code_item); |
| |
| StandardDexFile::CodeItem disk_code_item; |
| if (!reserve_only) { |
| disk_code_item.registers_size_ = code_item->RegistersSize(); |
| disk_code_item.ins_size_ = code_item->InsSize(); |
| disk_code_item.outs_size_ = code_item->OutsSize(); |
| disk_code_item.tries_size_ = code_item->TriesSize(); |
| disk_code_item.debug_info_off_ = code_item->DebugInfo() == nullptr |
| ? 0 |
| : code_item->DebugInfo()->GetOffset(); |
| disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize(); |
| } |
| // Avoid using sizeof so that we don't write the fake instruction array at the end of the code |
| // item. |
| stream->Write(&disk_code_item, OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_)); |
| // Write the instructions. |
| stream->Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t)); |
| // Write the post instruction data. |
| WriteCodeItemPostInstructionData(stream, code_item, reserve_only); |
| if (reserve_only) { |
| stream->Clear(start_offset, stream->Tell() - start_offset); |
| } |
| } |
| |
| void DexWriter::WriteCodeItems(Stream* stream, bool reserve_only) { |
| DexLayoutSection* code_section = nullptr; |
| if (!reserve_only && dex_layout_ != nullptr) { |
| code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>( |
| DexLayoutSections::SectionType::kSectionTypeCode)]; |
| } |
| const uint32_t start = stream->Tell(); |
| for (auto& code_item : header_->CodeItems()) { |
| uint32_t start_offset = stream->Tell(); |
| WriteCodeItem(stream, code_item.get(), reserve_only); |
| // Only add the section hotness info once. |
| if (!reserve_only && code_section != nullptr) { |
| auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); |
| if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { |
| code_section->parts_[static_cast<size_t>(it->second)].CombineSection( |
| start_offset, |
| stream->Tell()); |
| } |
| } |
| } |
| |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->CodeItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteClassDefs(Stream* stream, bool reserve_only) { |
| const uint32_t start = stream->Tell(); |
| uint32_t class_def_buffer[8]; |
| for (auto& class_def : header_->ClassDefs()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDefItem)); |
| if (reserve_only) { |
| stream->Skip(class_def->GetSize()); |
| } else { |
| class_def_buffer[0] = class_def->ClassType()->GetIndex(); |
| class_def_buffer[1] = class_def->GetAccessFlags(); |
| class_def_buffer[2] = class_def->Superclass() == nullptr ? dex::kDexNoIndex : |
| class_def->Superclass()->GetIndex(); |
| class_def_buffer[3] = class_def->InterfacesOffset(); |
| class_def_buffer[4] = class_def->SourceFile() == nullptr ? dex::kDexNoIndex : |
| class_def->SourceFile()->GetIndex(); |
| class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : |
| class_def->Annotations()->GetOffset(); |
| class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : |
| class_def->GetClassData()->GetOffset(); |
| class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : |
| class_def->StaticValues()->GetOffset(); |
| stream->Write(class_def_buffer, class_def->GetSize()); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->ClassDefs().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteClassDatas(Stream* stream) { |
| const uint32_t start = stream->Tell(); |
| for (const std::unique_ptr<dex_ir::ClassData>& class_data : |
| header_->ClassDatas()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeClassDataItem)); |
| ProcessOffset(stream, class_data.get()); |
| stream->WriteUleb128(class_data->StaticFields()->size()); |
| stream->WriteUleb128(class_data->InstanceFields()->size()); |
| stream->WriteUleb128(class_data->DirectMethods()->size()); |
| stream->WriteUleb128(class_data->VirtualMethods()->size()); |
| WriteEncodedFields(stream, class_data->StaticFields()); |
| WriteEncodedFields(stream, class_data->InstanceFields()); |
| WriteEncodedMethods(stream, class_data->DirectMethods()); |
| WriteEncodedMethods(stream, class_data->VirtualMethods()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->ClassDatas().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteCallSiteIds(Stream* stream, bool reserve_only) { |
| const uint32_t start = stream->Tell(); |
| uint32_t call_site_off[1]; |
| for (auto& call_site_id : header_->CallSiteIds()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeCallSiteIdItem)); |
| if (reserve_only) { |
| stream->Skip(call_site_id->GetSize()); |
| } else { |
| call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); |
| stream->Write(call_site_off, call_site_id->GetSize()); |
| } |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->CallSiteIds().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteMethodHandles(Stream* stream) { |
| const uint32_t start = stream->Tell(); |
| uint16_t method_handle_buff[4]; |
| for (auto& method_handle : header_->MethodHandleItems()) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeMethodHandleItem)); |
| method_handle_buff[0] = static_cast<uint16_t>(method_handle->GetMethodHandleType()); |
| method_handle_buff[1] = 0; // unused. |
| method_handle_buff[2] = method_handle->GetFieldOrMethodId()->GetIndex(); |
| method_handle_buff[3] = 0; // unused. |
| stream->Write(method_handle_buff, method_handle->GetSize()); |
| } |
| if (compute_offsets_ && start != stream->Tell()) { |
| header_->MethodHandleItems().SetOffset(start); |
| } |
| } |
| |
| void DexWriter::WriteMapItems(Stream* stream, MapItemQueue* queue) { |
| // All the sections should already have been added. |
| const uint32_t map_list_size = queue->size(); |
| stream->Write(&map_list_size, sizeof(map_list_size)); |
| while (!queue->empty()) { |
| const MapItem& item = queue->top(); |
| DexFile::MapItem map_item; |
| map_item.type_ = item.type_; |
| map_item.size_ = item.size_; |
| map_item.offset_ = item.offset_; |
| map_item.unused_ = 0u; |
| stream->Write(&map_item, sizeof(map_item)); |
| queue->pop(); |
| } |
| } |
| |
| void DexWriter::GenerateAndWriteMapItems(Stream* stream) { |
| MapItemQueue queue; |
| |
| // Header and index section. |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHeaderItem, 1, 0)); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringIdItem, |
| header_->StringIds().Size(), |
| header_->StringIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeIdItem, |
| header_->TypeIds().Size(), |
| header_->TypeIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeProtoIdItem, |
| header_->ProtoIds().Size(), |
| header_->ProtoIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeFieldIdItem, |
| header_->FieldIds().Size(), |
| header_->FieldIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodIdItem, |
| header_->MethodIds().Size(), |
| header_->MethodIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDefItem, |
| header_->ClassDefs().Size(), |
| header_->ClassDefs().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCallSiteIdItem, |
| header_->CallSiteIds().Size(), |
| header_->CallSiteIds().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodHandleItem, |
| header_->MethodHandleItems().Size(), |
| header_->MethodHandleItems().GetOffset())); |
| // Data section. |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, header_->MapListOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeList, |
| header_->TypeLists().Size(), |
| header_->TypeLists().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetRefList, |
| header_->AnnotationSetRefLists().Size(), |
| header_->AnnotationSetRefLists().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetItem, |
| header_->AnnotationSetItems().Size(), |
| header_->AnnotationSetItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDataItem, |
| header_->ClassDatas().Size(), |
| header_->ClassDatas().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCodeItem, |
| header_->CodeItems().Size(), |
| header_->CodeItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringDataItem, |
| header_->StringDatas().Size(), |
| header_->StringDatas().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeDebugInfoItem, |
| header_->DebugInfoItems().Size(), |
| header_->DebugInfoItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationItem, |
| header_->AnnotationItems().Size(), |
| header_->AnnotationItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeEncodedArrayItem, |
| header_->EncodedArrayItems().Size(), |
| header_->EncodedArrayItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, |
| header_->AnnotationsDirectoryItems().Size(), |
| header_->AnnotationsDirectoryItems().GetOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHiddenapiClassData, |
| header_->HiddenapiClassDatas().Empty() ? 0u : 1u, |
| header_->HiddenapiClassDatas().GetOffset())); |
| WriteMapItems(stream, &queue); |
| } |
| |
| void DexWriter::WriteHeader(Stream* stream) { |
| StandardDexFile::Header header; |
| if (CompactDexFile::IsMagicValid(header_->Magic())) { |
| StandardDexFile::WriteMagic(header.magic_); |
| // TODO: Should we write older versions based on the feature flags? |
| StandardDexFile::WriteCurrentVersion(header.magic_); |
| } else { |
| // Standard dex -> standard dex, just reuse the same header. |
| static constexpr size_t kMagicAndVersionLen = |
| StandardDexFile::kDexMagicSize + StandardDexFile::kDexVersionLen; |
| std::copy_n(header_->Magic(), kMagicAndVersionLen, header.magic_); |
| } |
| header.checksum_ = header_->Checksum(); |
| std::copy_n(header_->Signature(), DexFile::kSha1DigestSize, header.signature_); |
| header.file_size_ = header_->FileSize(); |
| header.header_size_ = GetHeaderSize(); |
| header.endian_tag_ = header_->EndianTag(); |
| header.link_size_ = header_->LinkSize(); |
| header.link_off_ = header_->LinkOffset(); |
| header.map_off_ = header_->MapListOffset(); |
| header.string_ids_size_ = header_->StringIds().Size(); |
| header.string_ids_off_ = header_->StringIds().GetOffset(); |
| header.type_ids_size_ = header_->TypeIds().Size(); |
| header.type_ids_off_ = header_->TypeIds().GetOffset(); |
| header.proto_ids_size_ = header_->ProtoIds().Size(); |
| header.proto_ids_off_ = header_->ProtoIds().GetOffset(); |
| header.field_ids_size_ = header_->FieldIds().Size(); |
| header.field_ids_off_ = header_->FieldIds().GetOffset(); |
| header.method_ids_size_ = header_->MethodIds().Size(); |
| header.method_ids_off_ = header_->MethodIds().GetOffset(); |
| header.class_defs_size_ = header_->ClassDefs().Size(); |
| header.class_defs_off_ = header_->ClassDefs().GetOffset(); |
| header.data_size_ = header_->DataSize(); |
| header.data_off_ = header_->DataOffset(); |
| |
| CHECK_EQ(sizeof(header), GetHeaderSize()); |
| static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec"); |
| stream->Seek(0); |
| stream->Overwrite(reinterpret_cast<uint8_t*>(&header), sizeof(header)); |
| } |
| |
| size_t DexWriter::GetHeaderSize() const { |
| return sizeof(StandardDexFile::Header); |
| } |
| |
| bool DexWriter::Write(DexContainer* output, std::string* error_msg) { |
| DCHECK(error_msg != nullptr); |
| |
| Stream stream_storage(output->GetMainSection()); |
| Stream* stream = &stream_storage; |
| |
| // Starting offset is right after the header. |
| stream->Seek(GetHeaderSize()); |
| |
| // Based on: https://source.android.com/devices/tech/dalvik/dex-format |
| // Since the offsets may not be calculated already, the writing must be done in the correct order. |
| const uint32_t string_ids_offset = stream->Tell(); |
| WriteStringIds(stream, /*reserve_only=*/ true); |
| WriteTypeIds(stream); |
| const uint32_t proto_ids_offset = stream->Tell(); |
| WriteProtoIds(stream, /*reserve_only=*/ true); |
| WriteFieldIds(stream); |
| WriteMethodIds(stream); |
| const uint32_t class_defs_offset = stream->Tell(); |
| WriteClassDefs(stream, /*reserve_only=*/ true); |
| const uint32_t call_site_ids_offset = stream->Tell(); |
| WriteCallSiteIds(stream, /*reserve_only=*/ true); |
| WriteMethodHandles(stream); |
| |
| uint32_t data_offset_ = 0u; |
| if (compute_offsets_) { |
| // Data section. |
| stream->AlignTo(kDataSectionAlignment); |
| data_offset_ = stream->Tell(); |
| } |
| |
| // Write code item first to minimize the space required for encoded methods. |
| // Reserve code item space since we need the debug offsets to actually write them. |
| const uint32_t code_items_offset = stream->Tell(); |
| WriteCodeItems(stream, /*reserve_only=*/ true); |
| // Write debug info section. |
| WriteDebugInfoItems(stream); |
| { |
| // Actually write code items since debug info offsets are calculated now. |
| Stream::ScopedSeek seek(stream, code_items_offset); |
| WriteCodeItems(stream, /*reserve_only=*/ false); |
| } |
| |
| WriteEncodedArrays(stream); |
| WriteAnnotations(stream); |
| WriteAnnotationSets(stream); |
| WriteAnnotationSetRefs(stream); |
| WriteAnnotationsDirectories(stream); |
| WriteTypeLists(stream); |
| WriteClassDatas(stream); |
| WriteStringDatas(stream); |
| WriteHiddenapiClassData(stream); |
| |
| // Write delayed id sections that depend on data sections. |
| { |
| Stream::ScopedSeek seek(stream, string_ids_offset); |
| WriteStringIds(stream, /*reserve_only=*/ false); |
| } |
| { |
| Stream::ScopedSeek seek(stream, proto_ids_offset); |
| WriteProtoIds(stream, /*reserve_only=*/ false); |
| } |
| { |
| Stream::ScopedSeek seek(stream, class_defs_offset); |
| WriteClassDefs(stream, /*reserve_only=*/ false); |
| } |
| { |
| Stream::ScopedSeek seek(stream, call_site_ids_offset); |
| WriteCallSiteIds(stream, /*reserve_only=*/ false); |
| } |
| |
| // Write the map list. |
| if (compute_offsets_) { |
| stream->AlignTo(SectionAlignment(DexFile::kDexTypeMapList)); |
| header_->SetMapListOffset(stream->Tell()); |
| } else { |
| stream->Seek(header_->MapListOffset()); |
| } |
| GenerateAndWriteMapItems(stream); |
| stream->AlignTo(kDataSectionAlignment); |
| |
| // Map items are included in the data section. |
| if (compute_offsets_) { |
| header_->SetDataSize(stream->Tell() - data_offset_); |
| if (header_->DataSize() != 0) { |
| // Offset must be zero when the size is zero. |
| header_->SetDataOffset(data_offset_); |
| } else { |
| header_->SetDataOffset(0u); |
| } |
| } |
| |
| // Write link data if it exists. |
| const std::vector<uint8_t>& link_data = header_->LinkData(); |
| if (link_data.size() > 0) { |
| CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size())); |
| if (compute_offsets_) { |
| header_->SetLinkOffset(stream->Tell()); |
| } else { |
| stream->Seek(header_->LinkOffset()); |
| } |
| stream->Write(&link_data[0], link_data.size()); |
| } |
| |
| // Write header last. |
| if (compute_offsets_) { |
| header_->SetFileSize(stream->Tell()); |
| } |
| WriteHeader(stream); |
| |
| if (dex_layout_->GetOptions().update_checksum_) { |
| header_->SetChecksum(DexFile::CalculateChecksum(stream->Begin(), header_->FileSize())); |
| // Rewrite the header with the calculated checksum. |
| WriteHeader(stream); |
| } |
| |
| // Trim the map to make it sized as large as the dex file. |
| output->GetMainSection()->Resize(header_->FileSize()); |
| return true; |
| } |
| |
| bool DexWriter::Output(DexLayout* dex_layout, |
| std::unique_ptr<DexContainer>* container, |
| bool compute_offsets, |
| std::string* error_msg) { |
| CHECK(dex_layout != nullptr); |
| std::unique_ptr<DexWriter> writer; |
| if (dex_layout->GetOptions().compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) { |
| CHECK(compute_offsets) << "Compact dex requires computing offsets"; |
| writer.reset(new CompactDexWriter(dex_layout)); |
| } else { |
| writer.reset(new DexWriter(dex_layout, compute_offsets)); |
| } |
| DCHECK(container != nullptr); |
| if (*container == nullptr) { |
| *container = writer->CreateDexContainer(); |
| } |
| return writer->Write(container->get(), error_msg); |
| } |
| |
| void MapItemQueue::AddIfNotEmpty(const MapItem& item) { |
| if (item.size_ != 0) { |
| push(item); |
| } |
| } |
| |
| void DexWriter::ProcessOffset(Stream* stream, dex_ir::Item* item) { |
| if (compute_offsets_) { |
| item->SetOffset(stream->Tell()); |
| } else { |
| // Not computing offsets, just use the one in the item. |
| stream->Seek(item->GetOffset()); |
| } |
| } |
| |
| std::unique_ptr<DexContainer> DexWriter::CreateDexContainer() const { |
| return std::unique_ptr<DexContainer>(new DexWriter::Container); |
| } |
| |
| } // namespace art |