| /* |
| * 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 "cdex/compact_dex_file.h" |
| #include "compact_dex_writer.h" |
| #include "dex_file_layout.h" |
| #include "dex_file_types.h" |
| #include "dexlayout.h" |
| #include "standard_dex_file.h" |
| #include "utf.h" |
| |
| namespace art { |
| |
| static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; |
| static constexpr uint32_t kDexSectionWordAlignment = 4; |
| |
| static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { |
| switch (type) { |
| case DexFile::kDexTypeClassDataItem: |
| case DexFile::kDexTypeStringDataItem: |
| case DexFile::kDexTypeDebugInfoItem: |
| case DexFile::kDexTypeAnnotationItem: |
| case DexFile::kDexTypeEncodedArrayItem: |
| return alignof(uint8_t); |
| |
| default: |
| // All other sections are kDexAlignedSection. |
| return kDexSectionWordAlignment; |
| } |
| } |
| |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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_; |
| }; |
| |
| 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_; |
| }; |
| |
| 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; |
| } |
| |
| size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) { |
| DCHECK_LE(offset + length, mem_map_->Size()); |
| memcpy(mem_map_->Begin() + offset, buffer, length); |
| return length; |
| } |
| |
| size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) { |
| uint8_t buffer[8]; |
| EncodeSignedLeb128(buffer, value); |
| return Write(buffer, SignedLeb128Size(value), offset); |
| } |
| |
| size_t DexWriter::WriteUleb128(uint32_t value, size_t offset) { |
| uint8_t buffer[8]; |
| EncodeUnsignedLeb128(buffer, value); |
| return Write(buffer, UnsignedLeb128Size(value), offset); |
| } |
| |
| size_t DexWriter::WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) { |
| size_t original_offset = offset; |
| 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: |
| offset += WriteEncodedValueHeader(type, 0, offset); |
| offset += WriteEncodedArray(encoded_value->GetEncodedArray()->GetEncodedValues(), offset); |
| return offset - original_offset; |
| case DexFile::kDexAnnotationAnnotation: |
| offset += WriteEncodedValueHeader(type, 0, offset); |
| offset += WriteEncodedAnnotation(encoded_value->GetEncodedAnnotation(), offset); |
| return offset - original_offset; |
| case DexFile::kDexAnnotationNull: |
| return WriteEncodedValueHeader(type, 0, offset); |
| case DexFile::kDexAnnotationBoolean: |
| return WriteEncodedValueHeader(type, encoded_value->GetBoolean() ? 1 : 0, offset); |
| default: |
| return 0; |
| } |
| offset += WriteEncodedValueHeader(type, length - 1, offset); |
| offset += Write(buffer + start, length, offset); |
| return offset - original_offset; |
| } |
| |
| size_t DexWriter::WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) { |
| uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) }; |
| return Write(buffer, sizeof(uint8_t), offset); |
| } |
| |
| size_t DexWriter::WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) { |
| size_t original_offset = offset; |
| offset += WriteUleb128(values->size(), offset); |
| for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) { |
| offset += WriteEncodedValue(value.get(), offset); |
| } |
| return offset - original_offset; |
| } |
| |
| size_t DexWriter::WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) { |
| size_t original_offset = offset; |
| offset += WriteUleb128(annotation->GetType()->GetIndex(), offset); |
| offset += WriteUleb128(annotation->GetAnnotationElements()->size(), offset); |
| for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element : |
| *annotation->GetAnnotationElements()) { |
| offset += WriteUleb128(annotation_element->GetName()->GetIndex(), offset); |
| offset += WriteEncodedValue(annotation_element->GetValue(), offset); |
| } |
| return offset - original_offset; |
| } |
| |
| size_t DexWriter::WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) { |
| size_t original_offset = offset; |
| uint32_t prev_index = 0; |
| for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) { |
| uint32_t index = field->GetFieldId()->GetIndex(); |
| offset += WriteUleb128(index - prev_index, offset); |
| offset += WriteUleb128(field->GetAccessFlags(), offset); |
| prev_index = index; |
| } |
| return offset - original_offset; |
| } |
| |
| size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) { |
| size_t original_offset = offset; |
| uint32_t prev_index = 0; |
| for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) { |
| uint32_t index = method->GetMethodId()->GetIndex(); |
| uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset(); |
| offset += WriteUleb128(index - prev_index, offset); |
| offset += WriteUleb128(method->GetAccessFlags(), offset); |
| offset += WriteUleb128(code_off, offset); |
| prev_index = index; |
| } |
| return offset - original_offset; |
| } |
| |
| // TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding |
| // function that takes a CollectionVector<T> and uses overloading. |
| uint32_t DexWriter::WriteStringIds(uint32_t offset, bool reserve_only) { |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringIdItem)); |
| if (reserve_only) { |
| offset += string_id->GetSize(); |
| } else { |
| uint32_t string_data_off = string_id->DataItem()->GetOffset(); |
| offset += Write(&string_data_off, string_id->GetSize(), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetStringIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteStringDatas(uint32_t offset) { |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::StringData>& string_data : header_->GetCollections().StringDatas()) { |
| ProcessOffset(&offset, string_data.get()); |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringDataItem)); |
| offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset); |
| // Skip null terminator (already zeroed out, no need to write). |
| offset += Write(string_data->Data(), strlen(string_data->Data()), offset) + 1u; |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetStringDatasOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteTypeIds(uint32_t offset) { |
| uint32_t descriptor_idx[1]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeIdItem)); |
| ProcessOffset(&offset, type_id.get()); |
| descriptor_idx[0] = type_id->GetStringId()->GetIndex(); |
| offset += Write(descriptor_idx, type_id->GetSize(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetTypeIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteTypeLists(uint32_t offset) { |
| uint32_t size[1]; |
| uint16_t list[1]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::TypeList>& type_list : header_->GetCollections().TypeLists()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeList)); |
| size[0] = type_list->GetTypeList()->size(); |
| ProcessOffset(&offset, type_list.get()); |
| offset += Write(size, sizeof(uint32_t), offset); |
| for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { |
| list[0] = type_id->GetIndex(); |
| offset += Write(list, sizeof(uint16_t), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetTypeListsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteProtoIds(uint32_t offset, bool reserve_only) { |
| uint32_t buffer[3]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeProtoIdItem)); |
| ProcessOffset(&offset, proto_id.get()); |
| if (reserve_only) { |
| offset += 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(); |
| offset += Write(buffer, proto_id->GetSize(), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetProtoIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteFieldIds(uint32_t offset) { |
| uint16_t buffer[4]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeFieldIdItem)); |
| ProcessOffset(&offset, 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; |
| offset += Write(buffer, field_id->GetSize(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetFieldIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteMethodIds(uint32_t offset) { |
| uint16_t buffer[4]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodIdItem)); |
| ProcessOffset(&offset, 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; |
| offset += Write(buffer, method_id->GetSize(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetMethodIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteEncodedArrays(uint32_t offset) { |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array : |
| header_->GetCollections().EncodedArrayItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeEncodedArrayItem)); |
| ProcessOffset(&offset, encoded_array.get()); |
| offset += WriteEncodedArray(encoded_array->GetEncodedValues(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetEncodedArrayItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteAnnotations(uint32_t offset) { |
| uint8_t visibility[1]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::AnnotationItem>& annotation : |
| header_->GetCollections().AnnotationItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationItem)); |
| visibility[0] = annotation->GetVisibility(); |
| ProcessOffset(&offset, annotation.get()); |
| offset += Write(visibility, sizeof(uint8_t), offset); |
| offset += WriteEncodedAnnotation(annotation->GetAnnotation(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetAnnotationItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteAnnotationSets(uint32_t offset) { |
| uint32_t size[1]; |
| uint32_t annotation_off[1]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set : |
| header_->GetCollections().AnnotationSetItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetItem)); |
| size[0] = annotation_set->GetItems()->size(); |
| ProcessOffset(&offset, annotation_set.get()); |
| offset += Write(size, sizeof(uint32_t), offset); |
| for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) { |
| annotation_off[0] = annotation->GetOffset(); |
| offset += Write(annotation_off, sizeof(uint32_t), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetAnnotationSetItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteAnnotationSetRefs(uint32_t offset) { |
| uint32_t size[1]; |
| uint32_t annotations_off[1]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref : |
| header_->GetCollections().AnnotationSetRefLists()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetRefList)); |
| size[0] = annotation_set_ref->GetItems()->size(); |
| ProcessOffset(&offset, annotation_set_ref.get()); |
| offset += Write(size, sizeof(uint32_t), offset); |
| for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) { |
| annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset(); |
| offset += Write(annotations_off, sizeof(uint32_t), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetAnnotationSetRefListsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteAnnotationsDirectories(uint32_t offset) { |
| uint32_t directory_buffer[4]; |
| uint32_t annotation_buffer[2]; |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory : |
| header_->GetCollections().AnnotationsDirectoryItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem)); |
| ProcessOffset(&offset, 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(); |
| offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset); |
| 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(); |
| offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); |
| } |
| } |
| 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(); |
| offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); |
| } |
| } |
| 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(); |
| offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset); |
| } |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) { |
| const uint32_t start = offset; |
| for (std::unique_ptr<dex_ir::DebugInfoItem>& debug_info : |
| header_->GetCollections().DebugInfoItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeDebugInfoItem)); |
| ProcessOffset(&offset, debug_info.get()); |
| offset += Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetDebugInfoItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteCodeItems(uint32_t offset, 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)]; |
| } |
| uint16_t uint16_buffer[4] = {}; |
| uint32_t uint32_buffer[2] = {}; |
| uint32_t start = offset; |
| for (auto& code_item : header_->GetCollections().CodeItems()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); |
| ProcessOffset(&offset, code_item.get()); |
| if (!reserve_only) { |
| uint16_buffer[0] = code_item->RegistersSize(); |
| uint16_buffer[1] = code_item->InsSize(); |
| uint16_buffer[2] = code_item->OutsSize(); |
| uint16_buffer[3] = code_item->TriesSize(); |
| uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : |
| code_item->DebugInfo()->GetOffset(); |
| uint32_buffer[1] = code_item->InsnsSize(); |
| // Only add the section hotness info once. |
| if (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( |
| code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize()); |
| } |
| } |
| } |
| offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); |
| offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); |
| offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); |
| if (code_item->TriesSize() != 0) { |
| if (code_item->InsnsSize() % 2 != 0) { |
| uint16_t padding[1] = { 0 }; |
| offset += Write(padding, sizeof(uint16_t), offset); |
| } |
| uint32_t start_addr[1]; |
| uint16_t insn_count_and_handler_off[2]; |
| for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { |
| start_addr[0] = try_item->StartAddr(); |
| insn_count_and_handler_off[0] = try_item->InsnCount(); |
| insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset(); |
| offset += Write(start_addr, sizeof(uint32_t), offset); |
| offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); |
| } |
| // Leave offset pointing to the end of the try items. |
| UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); |
| for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { |
| size_t list_offset = offset + handlers->GetListOffset(); |
| uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : |
| handlers->GetHandlers()->size(); |
| list_offset += WriteSleb128(size, list_offset); |
| for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { |
| if (handler->GetTypeId() != nullptr) { |
| list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); |
| } |
| list_offset += WriteUleb128(handler->GetAddress(), list_offset); |
| } |
| } |
| } |
| // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change. |
| offset = code_item->GetOffset() + code_item->GetSize(); |
| } |
| |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetCodeItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteClassDefs(uint32_t offset, bool reserve_only) { |
| const uint32_t start = offset; |
| uint32_t class_def_buffer[8]; |
| for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDefItem)); |
| if (reserve_only) { |
| offset += 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(); |
| offset += Write(class_def_buffer, class_def->GetSize(), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetClassDefsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteClassDatas(uint32_t offset) { |
| const uint32_t start = offset; |
| for (const std::unique_ptr<dex_ir::ClassData>& class_data : |
| header_->GetCollections().ClassDatas()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDataItem)); |
| ProcessOffset(&offset, class_data.get()); |
| offset += WriteUleb128(class_data->StaticFields()->size(), offset); |
| offset += WriteUleb128(class_data->InstanceFields()->size(), offset); |
| offset += WriteUleb128(class_data->DirectMethods()->size(), offset); |
| offset += WriteUleb128(class_data->VirtualMethods()->size(), offset); |
| offset += WriteEncodedFields(class_data->StaticFields(), offset); |
| offset += WriteEncodedFields(class_data->InstanceFields(), offset); |
| offset += WriteEncodedMethods(class_data->DirectMethods(), offset); |
| offset += WriteEncodedMethods(class_data->VirtualMethods(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetClassDatasOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteCallSiteIds(uint32_t offset, bool reserve_only) { |
| const uint32_t start = offset; |
| uint32_t call_site_off[1]; |
| for (std::unique_ptr<dex_ir::CallSiteId>& call_site_id : |
| header_->GetCollections().CallSiteIds()) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCallSiteIdItem)); |
| if (reserve_only) { |
| offset += call_site_id->GetSize(); |
| } else { |
| call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); |
| offset += Write(call_site_off, call_site_id->GetSize(), offset); |
| } |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetCallSiteIdsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteMethodHandles(uint32_t offset) { |
| const uint32_t start = offset; |
| uint16_t method_handle_buff[4]; |
| for (std::unique_ptr<dex_ir::MethodHandleItem>& method_handle : |
| header_->GetCollections().MethodHandleItems()) { |
| offset = RoundUp(offset, 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. |
| offset += Write(method_handle_buff, method_handle->GetSize(), offset); |
| } |
| if (compute_offsets_ && start != offset) { |
| header_->GetCollections().SetMethodHandleItemsOffset(start); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::WriteMapItems(uint32_t offset, MapItemQueue* queue) { |
| // All the sections should already have been added. |
| uint16_t uint16_buffer[2]; |
| uint32_t uint32_buffer[2]; |
| uint16_buffer[1] = 0; |
| uint32_buffer[0] = queue->size(); |
| const uint32_t start = offset; |
| offset += Write(uint32_buffer, sizeof(uint32_t), offset); |
| while (!queue->empty()) { |
| const MapItem& map_item = queue->top(); |
| uint16_buffer[0] = map_item.type_; |
| uint32_buffer[0] = map_item.size_; |
| uint32_buffer[1] = map_item.offset_; |
| offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset); |
| offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); |
| queue->pop(); |
| } |
| return offset - start; |
| } |
| |
| uint32_t DexWriter::GenerateAndWriteMapItems(uint32_t offset) { |
| dex_ir::Collections& collection = header_->GetCollections(); |
| MapItemQueue queue; |
| |
| // Header and index section. |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHeaderItem, 1, 0)); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringIdItem, |
| collection.StringIdsSize(), |
| collection.StringIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeIdItem, |
| collection.TypeIdsSize(), |
| collection.TypeIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeProtoIdItem, |
| collection.ProtoIdsSize(), |
| collection.ProtoIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeFieldIdItem, |
| collection.FieldIdsSize(), |
| collection.FieldIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodIdItem, |
| collection.MethodIdsSize(), |
| collection.MethodIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDefItem, |
| collection.ClassDefsSize(), |
| collection.ClassDefsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCallSiteIdItem, |
| collection.CallSiteIdsSize(), |
| collection.CallSiteIdsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodHandleItem, |
| collection.MethodHandleItemsSize(), |
| collection.MethodHandleItemsOffset())); |
| // Data section. |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeList, |
| collection.TypeListsSize(), |
| collection.TypeListsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetRefList, |
| collection.AnnotationSetRefListsSize(), |
| collection.AnnotationSetRefListsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetItem, |
| collection.AnnotationSetItemsSize(), |
| collection.AnnotationSetItemsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDataItem, |
| collection.ClassDatasSize(), |
| collection.ClassDatasOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCodeItem, |
| collection.CodeItemsSize(), |
| collection.CodeItemsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringDataItem, |
| collection.StringDatasSize(), |
| collection.StringDatasOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeDebugInfoItem, |
| collection.DebugInfoItemsSize(), |
| collection.DebugInfoItemsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationItem, |
| collection.AnnotationItemsSize(), |
| collection.AnnotationItemsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeEncodedArrayItem, |
| collection.EncodedArrayItemsSize(), |
| collection.EncodedArrayItemsOffset())); |
| queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, |
| collection.AnnotationsDirectoryItemsSize(), |
| collection.AnnotationsDirectoryItemsOffset())); |
| |
| // Write the map items. |
| return WriteMapItems(offset, &queue); |
| } |
| |
| void DexWriter::WriteHeader() { |
| StandardDexFile::Header 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_ = header_->GetSize(); |
| header.endian_tag_ = header_->EndianTag(); |
| header.link_size_ = header_->LinkSize(); |
| header.link_off_ = header_->LinkOffset(); |
| const dex_ir::Collections& collections = header_->GetCollections(); |
| header.map_off_ = collections.MapListOffset(); |
| header.string_ids_size_ = collections.StringIdsSize(); |
| header.string_ids_off_ = collections.StringIdsOffset(); |
| header.type_ids_size_ = collections.TypeIdsSize(); |
| header.type_ids_off_ = collections.TypeIdsOffset(); |
| header.proto_ids_size_ = collections.ProtoIdsSize(); |
| header.proto_ids_off_ = collections.ProtoIdsOffset(); |
| header.field_ids_size_ = collections.FieldIdsSize(); |
| header.field_ids_off_ = collections.FieldIdsOffset(); |
| header.method_ids_size_ = collections.MethodIdsSize(); |
| header.method_ids_off_ = collections.MethodIdsOffset(); |
| header.class_defs_size_ = collections.ClassDefsSize(); |
| header.class_defs_off_ = collections.ClassDefsOffset(); |
| header.data_size_ = header_->DataSize(); |
| header.data_off_ = header_->DataOffset(); |
| |
| static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec"); |
| UNUSED(Write(reinterpret_cast<uint8_t*>(&header), sizeof(header), 0u)); |
| } |
| |
| void DexWriter::WriteMemMap() { |
| // Starting offset is right after the header. |
| uint32_t offset = sizeof(StandardDexFile::Header); |
| |
| dex_ir::Collections& collection = header_->GetCollections(); |
| |
| // 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 = offset; |
| offset += WriteStringIds(offset, /*reserve_only*/ true); |
| offset += WriteTypeIds(offset); |
| const uint32_t proto_ids_offset = offset; |
| offset += WriteProtoIds(offset, /*reserve_only*/ true); |
| offset += WriteFieldIds(offset); |
| offset += WriteMethodIds(offset); |
| const uint32_t class_defs_offset = offset; |
| offset += WriteClassDefs(offset, /*reserve_only*/ true); |
| const uint32_t call_site_ids_offset = offset; |
| offset += WriteCallSiteIds(offset, /*reserve_only*/ true); |
| offset += WriteMethodHandles(offset); |
| |
| uint32_t data_offset_ = 0u; |
| if (compute_offsets_) { |
| // Data section. |
| offset = RoundUp(offset, kDataSectionAlignment); |
| data_offset_ = offset; |
| } |
| |
| // 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 = offset; |
| offset += WriteCodeItems(offset, /*reserve_only*/ true); |
| // Write debug info section. |
| offset += WriteDebugInfoItems(offset); |
| // Actually write code items since debug info offsets are calculated now. |
| WriteCodeItems(code_items_offset, /*reserve_only*/ false); |
| |
| offset += WriteEncodedArrays(offset); |
| offset += WriteAnnotations(offset); |
| offset += WriteAnnotationSets(offset); |
| offset += WriteAnnotationSetRefs(offset); |
| offset += WriteAnnotationsDirectories(offset); |
| offset += WriteTypeLists(offset); |
| offset += WriteClassDatas(offset); |
| offset += WriteStringDatas(offset); |
| |
| // Write delayed id sections that depend on data sections. |
| WriteStringIds(string_ids_offset, /*reserve_only*/ false); |
| WriteProtoIds(proto_ids_offset, /*reserve_only*/ false); |
| WriteClassDefs(class_defs_offset, /*reserve_only*/ false); |
| WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false); |
| |
| // Write the map list. |
| if (compute_offsets_) { |
| offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList)); |
| collection.SetMapListOffset(offset); |
| } else { |
| offset = collection.MapListOffset(); |
| } |
| offset += GenerateAndWriteMapItems(offset); |
| offset = RoundUp(offset, kDataSectionAlignment); |
| |
| // Map items are included in the data section. |
| if (compute_offsets_) { |
| header_->SetDataSize(offset - 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 = collection.LinkData(); |
| if (link_data.size() > 0) { |
| CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size())); |
| if (compute_offsets_) { |
| header_->SetLinkOffset(offset); |
| } |
| offset += Write(&link_data[0], link_data.size(), header_->LinkOffset()); |
| } |
| |
| // Write header last. |
| if (compute_offsets_) { |
| header_->SetFileSize(offset); |
| } |
| WriteHeader(); |
| } |
| |
| void DexWriter::Output(dex_ir::Header* header, |
| MemMap* mem_map, |
| DexLayout* dex_layout, |
| bool compute_offsets, |
| CompactDexLevel compact_dex_level) { |
| CHECK(dex_layout != nullptr); |
| std::unique_ptr<DexWriter> writer; |
| if (compact_dex_level != CompactDexLevel::kCompactDexLevelNone) { |
| writer.reset(new CompactDexWriter(header, mem_map, dex_layout, compact_dex_level)); |
| } else { |
| writer.reset(new DexWriter(header, mem_map, dex_layout, compute_offsets)); |
| } |
| writer->WriteMemMap(); |
| } |
| |
| void MapItemQueue::AddIfNotEmpty(const MapItem& item) { |
| if (item.size_ != 0) { |
| push(item); |
| } |
| } |
| |
| } // namespace art |