/* * Copyright (C) 2017 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 "slicer/writer.h" #include "slicer/common.h" #include "slicer/scopeguard.h" #include "slicer/dex_bytecode.h" #include "slicer/dex_format.h" #include "slicer/dex_ir.h" #include "slicer/dex_leb128.h" #include #include #include #include #include #include namespace dex { // Returns the IR node index, or kNoIndex for null IR nodes template static dex::u4 OptIndex(const T* ir_node) { return ir_node != nullptr ? ir_node->index : dex::kNoIndex; } // Helper for creating the header of an encoded value static void WriteEncodedValueHeader(dex::u1 type, int arg, Section& data) { assert((type & ~dex::kEncodedValueTypeMask) == 0); assert(arg >= 0 && arg < 8); dex::u1 header = dex::u1(type | (arg << dex::kEncodedValueArgShift)); data.Push(header); } // Writes an integer encoded value template static void WriteIntValue(dex::u1 type, T value, Section& data) { dex::u1 buff[sizeof(T)] = {}; dex::u1* dst = buff; if (std::is_signed::value) { const bool positive = (value >= 0); while (positive ? value >= 0x80 : value < -0x80) { *dst++ = value & 0xff; value >>= 8; } *dst++ = value & 0xff; } else { do { *dst++ = value & 0xff; value >>= 8; } while (value != 0); } size_t size = dst - buff; assert(size > 0 && size <= sizeof(T)); WriteEncodedValueHeader(type, size - 1, data); data.Push(buff, size); } // Writes a floating point encoded value template static void WriteFloatValue(dex::u1 type, T value, Section& data) { dex::u1 buff[sizeof(T)] = {}; auto src = reinterpret_cast(&value); size_t size = sizeof(T); // skip "rightmost" zero bytes while (size > 1 && *src == 0) { --size; ++src; } // copy the rest... for (size_t i = 0; i < size; ++i) { buff[i] = src[i]; } assert(size > 0 && size <= sizeof(T)); WriteEncodedValueHeader(type, size - 1, data); data.Push(buff, size); } static void WriteEncodedArray(const ir::EncodedArray* ir_array, Section& data); static void WriteAnnotation(const ir::Annotation* ir_annotation, Section& data); // "encoded_value" static void WriteEncodedValue(const ir::EncodedValue* ir_value, Section& data) { SLICER_EXTRA(auto offset = data.size()); dex::u1 type = ir_value->type; switch (type) { case dex::kEncodedByte: WriteIntValue(type, ir_value->u.byte_value, data); break; case dex::kEncodedShort: WriteIntValue(type, ir_value->u.short_value, data); break; case dex::kEncodedChar: WriteIntValue(type, ir_value->u.char_value, data); break; case dex::kEncodedInt: WriteIntValue(type, ir_value->u.int_value, data); break; case dex::kEncodedLong: WriteIntValue(type, ir_value->u.long_value, data); break; case dex::kEncodedFloat: WriteFloatValue(type, ir_value->u.float_value, data); break; case dex::kEncodedDouble: WriteFloatValue(type, ir_value->u.double_value, data); break; case dex::kEncodedString: WriteIntValue(type, ir_value->u.string_value->index, data); break; case dex::kEncodedType: WriteIntValue(type, ir_value->u.type_value->index, data); break; case dex::kEncodedField: WriteIntValue(type, ir_value->u.field_value->index, data); break; case dex::kEncodedMethod: WriteIntValue(type, ir_value->u.method_value->index, data); break; case dex::kEncodedEnum: WriteIntValue(type, ir_value->u.enum_value->index, data); break; case dex::kEncodedArray: WriteEncodedValueHeader(type, 0, data); WriteEncodedArray(ir_value->u.array_value, data); break; case dex::kEncodedAnnotation: WriteEncodedValueHeader(type, 0, data); WriteAnnotation(ir_value->u.annotation_value, data); break; case dex::kEncodedNull: WriteEncodedValueHeader(type, 0, data); break; case dex::kEncodedBoolean: { int arg = ir_value->u.bool_value ? 1 : 0; WriteEncodedValueHeader(type, arg, data); } break; default: SLICER_CHECK(!"unexpected value type"); } // optionally check the encoding against the original one // (if possible, some of the values contain relocated indexes) SLICER_EXTRA({ switch (type) { case dex::kEncodedByte: case dex::kEncodedShort: case dex::kEncodedChar: case dex::kEncodedInt: case dex::kEncodedLong: case dex::kEncodedFloat: case dex::kEncodedDouble: case dex::kEncodedNull: case dex::kEncodedBoolean: auto ptr = data.ptr(offset); auto size = data.size() - offset; SLICER_CHECK(size == ir_value->original.size()); SLICER_CHECK(memcmp(ptr, ir_value->original.ptr(), size) == 0); break; } }); } // "encoded_annotation" static void WriteAnnotation(const ir::Annotation* ir_annotation, Section& data) { data.PushULeb128(ir_annotation->type->index); data.PushULeb128(ir_annotation->elements.size()); for (auto irAnnotationElement : ir_annotation->elements) { data.PushULeb128(irAnnotationElement->name->index); WriteEncodedValue(irAnnotationElement->value, data); } } // "encoded_array" static void WriteEncodedArray(const ir::EncodedArray* ir_array, Section& data) { const auto& values = ir_array->values; data.PushULeb128(values.size()); for (auto irEncodedValue : values) { WriteEncodedValue(irEncodedValue, data); } } // helper for concatenating .dex sections into the final image template static void CopySection(const T& section, dex::u1* image, dex::u4 image_size) { if (section.size() == 0) { SLICER_CHECK(section.ItemsCount() == 0); return; } SLICER_CHECK(section.ItemsCount() > 0); dex::u4 offset = section.SectionOffset(); dex::u4 size = section.size(); SLICER_CHECK(offset >= sizeof(dex::Header)); SLICER_CHECK(offset + size <= image_size); ::memcpy(image + offset, section.data(), size); } static u4 ReadU4(const u2* ptr) { return ptr[0] | (u4(ptr[1]) << 16); } static void WriteU4(u2* ptr, u4 val) { ptr[0] = val & 0xffff; ptr[1] = val >> 16; } // This is the main interface for the .dex writer // (returns nullptr on failure) dex::u1* Writer::CreateImage(Allocator* allocator, size_t* new_image_size) { // create a new DexImage dex_.reset(new DexImage); SLICER_SCOPE_EXIT { dex_.reset(); }; // TODO: revisit IR normalization // (ideally we shouldn't change the IR while generating an image) dex_ir_->Normalize(); // track the current offset within the .dex image dex::u4 offset = 0; // allocate the image and index sections // (they will be back-filled) offset += sizeof(dex::Header); offset += dex_->string_ids.Init(offset, dex_ir_->strings.size()); offset += dex_->type_ids.Init(offset, dex_ir_->types.size()); offset += dex_->proto_ids.Init(offset, dex_ir_->protos.size()); offset += dex_->field_ids.Init(offset, dex_ir_->fields.size()); offset += dex_->method_ids.Init(offset, dex_ir_->methods.size()); offset += dex_->class_defs.Init(offset, dex_ir_->classes.size()); // the base offset for the "data" meta-section SLICER_CHECK(offset % 4 == 0); const dex::u4 data_offset = offset; // we must create the sections in a very specific // order due to file pointers across sections offset += CreateStringDataSection(offset); offset += CreateTypeListsSection(offset); offset += CreateDebugInfoSection(offset); offset += CreateEncodedArrayItemSection(offset); offset += CreateCodeItemSection(offset); offset += CreateClassDataSection(offset); offset += CreateAnnItemSection(offset); offset += CreateAnnSetsSection(offset); offset += CreateAnnSetRefListsSection(offset); offset += CreateAnnDirectoriesSection(offset); offset += CreateMapSection(offset); // back-fill the indexes FillTypes(); FillFields(); FillProtos(); FillMethods(); FillClassDefs(); // allocate the final buffer for the .dex image SLICER_CHECK(offset % 4 == 0); const dex::u4 image_size = offset; dex::u1* image = static_cast(allocator->Allocate(image_size)); if (image == nullptr) { // memory allocation failed, bailing out... return nullptr; } memset(image, 0, image_size); // finally, back-fill the header SLICER_CHECK(image_size > sizeof(dex::Header)); dex::Header* header = reinterpret_cast(image + 0); // magic signature memcpy(header->magic, dex_ir_->magic.ptr(), dex_ir_->magic.size()); header->file_size = image_size; header->header_size = sizeof(dex::Header); header->endian_tag = dex::kEndianConstant; header->link_size = 0; header->link_off = 0; header->map_off = dex_->map_list.SectionOffset(); header->string_ids_size = dex_->string_ids.ItemsCount(); header->string_ids_off = dex_->string_ids.SectionOffset(); header->type_ids_size = dex_->type_ids.ItemsCount(); header->type_ids_off = dex_->type_ids.SectionOffset(); header->proto_ids_size = dex_->proto_ids.ItemsCount(); header->proto_ids_off = dex_->proto_ids.SectionOffset(); header->field_ids_size = dex_->field_ids.ItemsCount(); header->field_ids_off = dex_->field_ids.SectionOffset(); header->method_ids_size = dex_->method_ids.ItemsCount(); header->method_ids_off = dex_->method_ids.SectionOffset(); header->class_defs_size = dex_->class_defs.ItemsCount(); header->class_defs_off = dex_->class_defs.SectionOffset(); header->data_size = image_size - data_offset; header->data_off = data_offset; // copy the individual sections to the final image CopySection(dex_->string_ids, image, image_size); CopySection(dex_->type_ids, image, image_size); CopySection(dex_->proto_ids, image, image_size); CopySection(dex_->field_ids, image, image_size); CopySection(dex_->method_ids, image, image_size); CopySection(dex_->class_defs, image, image_size); CopySection(dex_->string_data, image, image_size); CopySection(dex_->type_lists, image, image_size); CopySection(dex_->debug_info, image, image_size); CopySection(dex_->encoded_arrays, image, image_size); CopySection(dex_->code, image, image_size); CopySection(dex_->class_data, image, image_size); CopySection(dex_->ann_directories, image, image_size); CopySection(dex_->ann_set_ref_lists, image, image_size); CopySection(dex_->ann_sets, image, image_size); CopySection(dex_->ann_items, image, image_size); CopySection(dex_->map_list, image, image_size); // checksum header->checksum = dex::ComputeChecksum(header); *new_image_size = image_size; return image; } // "string_id_item" + string data section dex::u4 Writer::CreateStringDataSection(dex::u4 section_offset) { auto& section = dex_->string_data; section.SetOffset(section_offset); const auto& strings = dex_ir_->strings; for (size_t i = 0; i < strings.size(); ++i) { const auto& ir_string = strings[i]; auto dexStringId = &dex_->string_ids[i]; dex::u4 offset = section.AddItem(); section.Push(ir_string->data); dexStringId->string_data_off = section.AbsoluteOffset(offset); } dex::u4 size = section.Seal(4); return size; } // Helper for creating the map section template static void AddMapItem(const T& section, std::vector& items) { if (section.ItemsCount() > 0) { SLICER_CHECK(section.SectionOffset() >= sizeof(dex::Header)); dex::MapItem map_item = {}; map_item.type = section.MapEntryType(); map_item.size = section.ItemsCount(); map_item.offset = section.SectionOffset(); items.push_back(map_item); } } // map_list section dex::u4 Writer::CreateMapSection(dex::u4 section_offset) { auto& section = dex_->map_list; section.SetOffset(section_offset); section.AddItem(4); std::vector map_items; dex::MapItem headerItem = {}; headerItem.type = dex::kHeaderItem; headerItem.size = 1; headerItem.offset = 0; map_items.push_back(headerItem); AddMapItem(dex_->string_ids, map_items); AddMapItem(dex_->type_ids, map_items); AddMapItem(dex_->proto_ids, map_items); AddMapItem(dex_->field_ids, map_items); AddMapItem(dex_->method_ids, map_items); AddMapItem(dex_->class_defs, map_items); AddMapItem(dex_->string_data, map_items); AddMapItem(dex_->type_lists, map_items); AddMapItem(dex_->debug_info, map_items); AddMapItem(dex_->encoded_arrays, map_items); AddMapItem(dex_->code, map_items); AddMapItem(dex_->class_data, map_items); AddMapItem(dex_->ann_directories, map_items); AddMapItem(dex_->ann_set_ref_lists, map_items); AddMapItem(dex_->ann_sets, map_items); AddMapItem(dex_->ann_items, map_items); AddMapItem(dex_->map_list, map_items); std::sort(map_items.begin(), map_items.end(), [](const dex::MapItem& a, const dex::MapItem& b) { SLICER_CHECK(a.offset != b.offset); return a.offset < b.offset; }); section.Push(map_items.size()); section.Push(map_items); return section.Seal(4); } // annotation_item section dex::u4 Writer::CreateAnnItemSection(dex::u4 section_offset) { dex_->ann_items.SetOffset(section_offset); for (const auto& ir_node : dex_ir_->annotations) { if (ir_node->visibility != dex::kVisibilityEncoded) { // TODO: factor out the node_offset_ updating dex::u4& offset = node_offset_[ir_node.get()]; SLICER_CHECK(offset == 0); offset = WriteAnnotationItem(ir_node.get()); } } return dex_->ann_items.Seal(4); } // annotation_set_item section dex::u4 Writer::CreateAnnSetsSection(dex::u4 section_offset) { dex_->ann_sets.SetOffset(section_offset); for (const auto& ir_node : dex_ir_->annotation_sets) { dex::u4& offset = node_offset_[ir_node.get()]; SLICER_CHECK(offset == 0); offset = WriteAnnotationSet(ir_node.get()); } return dex_->ann_sets.Seal(4); } // annotation_set_ref_list section dex::u4 Writer::CreateAnnSetRefListsSection(dex::u4 section_offset) { dex_->ann_set_ref_lists.SetOffset(section_offset); for (const auto& ir_node : dex_ir_->annotation_set_ref_lists) { dex::u4& offset = node_offset_[ir_node.get()]; SLICER_CHECK(offset == 0); offset = WriteAnnotationSetRefList(ir_node.get()); } return dex_->ann_set_ref_lists.Seal(4); } // type_list section dex::u4 Writer::CreateTypeListsSection(dex::u4 section_offset) { dex_->type_lists.SetOffset(section_offset); for (const auto& ir_type_list : dex_ir_->type_lists) { dex::u4& offset = node_offset_[ir_type_list.get()]; SLICER_CHECK(offset == 0); offset = WriteTypeList(ir_type_list->types); } return dex_->type_lists.Seal(4); } // code_item section dex::u4 Writer::CreateCodeItemSection(dex::u4 section_offset) { dex_->code.SetOffset(section_offset); for (const auto& ir_node : dex_ir_->code) { dex::u4& offset = node_offset_[ir_node.get()]; SLICER_CHECK(offset == 0); offset = WriteCode(ir_node.get()); } dex::u4 size = dex_->code.Seal(4); return size; } // debug info section dex::u4 Writer::CreateDebugInfoSection(dex::u4 section_offset) { dex_->debug_info.SetOffset(section_offset); for (const auto& ir_node : dex_ir_->debug_info) { dex::u4& offset = node_offset_[ir_node.get()]; SLICER_CHECK(offset == 0); offset = WriteDebugInfo(ir_node.get()); } dex::u4 size = dex_->debug_info.Seal(4); return size; } // class_data_item section dex::u4 Writer::CreateClassDataSection(dex::u4 section_offset) { dex_->class_data.SetOffset(section_offset); const auto& classes = dex_ir_->classes; for (size_t i = 0; i < classes.size(); ++i) { auto ir_class = classes[i].get(); auto dex_class_def = &dex_->class_defs[i]; dex_class_def->class_data_off = WriteClassData(ir_class); } dex::u4 size = dex_->class_data.Seal(4); return size; } // annotations_directory section dex::u4 Writer::CreateAnnDirectoriesSection(dex::u4 section_offset) { dex_->ann_directories.SetOffset(section_offset); const auto& classes = dex_ir_->classes; for (size_t i = 0; i < classes.size(); ++i) { auto ir_class = classes[i].get(); auto dex_class_def = &dex_->class_defs[i]; dex_class_def->annotations_off = WriteClassAnnotations(ir_class); } return dex_->ann_directories.Seal(4); } // encoded_array_item section dex::u4 Writer::CreateEncodedArrayItemSection(dex::u4 section_offset) { dex_->encoded_arrays.SetOffset(section_offset); const auto& classes = dex_ir_->classes; for (size_t i = 0; i < classes.size(); ++i) { auto ir_class = classes[i].get(); auto dex_class_def = &dex_->class_defs[i]; dex_class_def->static_values_off = WriteClassStaticValues(ir_class); } return dex_->encoded_arrays.Seal(4); } // "type_id_item" void Writer::FillTypes() { const auto& types = dex_ir_->types; for (size_t i = 0; i < types.size(); ++i) { const auto& ir_type = types[i]; auto dexTypeId = &dex_->type_ids[i]; // CONSIDER: an automatic index check would be nice dexTypeId->descriptor_idx = ir_type->descriptor->index; } } // "proto_id_item" void Writer::FillProtos() { const auto& protos = dex_ir_->protos; for (size_t i = 0; i < protos.size(); ++i) { const auto& irProto = protos[i]; auto dexProtoId = &dex_->proto_ids[i]; dexProtoId->shorty_idx = irProto->shorty->index; dexProtoId->return_type_idx = irProto->return_type->index; dexProtoId->parameters_off = FilePointer(irProto->param_types); } } // "field_id_item" void Writer::FillFields() { const auto& fields = dex_ir_->fields; for (size_t i = 0; i < fields.size(); ++i) { const auto& ir_field = fields[i]; auto dexFieldId = &dex_->field_ids[i]; dexFieldId->class_idx = ir_field->parent->index; dexFieldId->type_idx = ir_field->type->index; dexFieldId->name_idx = ir_field->name->index; } } // "method_id_item" void Writer::FillMethods() { const auto& methods = dex_ir_->methods; for (size_t i = 0; i < methods.size(); ++i) { const auto& ir_method = methods[i]; auto dexMethodId = &dex_->method_ids[i]; dexMethodId->class_idx = ir_method->parent->index; dexMethodId->proto_idx = ir_method->prototype->index; dexMethodId->name_idx = ir_method->name->index; } } // "class_def_item" void Writer::FillClassDefs() { const auto& classes = dex_ir_->classes; for (size_t i = 0; i < classes.size(); ++i) { auto ir_class = classes[i].get(); auto dex_class_def = &dex_->class_defs[i]; dex_class_def->class_idx = ir_class->type->index; dex_class_def->access_flags = ir_class->access_flags; dex_class_def->superclass_idx = OptIndex(ir_class->super_class); dex_class_def->source_file_idx = OptIndex(ir_class->source_file); dex_class_def->interfaces_off = FilePointer(ir_class->interfaces); // NOTE: we already set some offsets when we created the // corresponding .dex section: // // ->annotations_off // ->class_data_off // ->static_values_off } } // "type_list" dex::u4 Writer::WriteTypeList(const std::vector& types) { if (types.empty()) { return 0; } auto& data = dex_->type_lists; dex::u4 offset = data.AddItem(4); data.Push(types.size()); for (auto ir_type : types) { data.Push(ir_type->index); } return data.AbsoluteOffset(offset); } // "annotation_item" dex::u4 Writer::WriteAnnotationItem(const ir::Annotation* ir_annotation) { SLICER_CHECK(ir_annotation->visibility != dex::kVisibilityEncoded); auto& data = dex_->ann_items; dex::u4 offset = data.AddItem(); data.Push(ir_annotation->visibility); WriteAnnotation(ir_annotation, data); return data.AbsoluteOffset(offset); } // "annotation_set_item" dex::u4 Writer::WriteAnnotationSet(const ir::AnnotationSet* ir_annotation_set) { SLICER_CHECK(ir_annotation_set != nullptr); const auto& annotations = ir_annotation_set->annotations; auto& data = dex_->ann_sets; dex::u4 offset = data.AddItem(4); data.Push(annotations.size()); for (auto ir_annotation : annotations) { data.Push(FilePointer(ir_annotation)); } return data.AbsoluteOffset(offset); } // "annotation_set_ref_list" dex::u4 Writer::WriteAnnotationSetRefList( const ir::AnnotationSetRefList* ir_annotation_set_ref_list) { SLICER_CHECK(ir_annotation_set_ref_list != nullptr); const auto& annotations = ir_annotation_set_ref_list->annotations; auto& data = dex_->ann_set_ref_lists; dex::u4 offset = data.AddItem(4); data.Push(annotations.size()); for (auto ir_annotation_set : annotations) { data.Push(FilePointer(ir_annotation_set)); } return data.AbsoluteOffset(offset); } // "annotations_directory_item" dex::u4 Writer::WriteClassAnnotations(const ir::Class* ir_class) { if (ir_class->annotations == nullptr) { return 0; } auto ir_annotations = ir_class->annotations; dex::u4& offset = node_offset_[ir_annotations]; if (offset == 0) { // in order to write a contiguous "annotations_directory_item" we do two // passes : // 1. write the field/method/params annotations content // 2. write the directory (including the field/method/params arrays) std::vector dex_field_annotations; std::vector dex_method_annotations; std::vector dex_param_annotations; for (auto irItem : ir_annotations->field_annotations) { dex::FieldAnnotationsItem dex_item = {}; dex_item.field_idx = irItem->field_decl->index; dex_item.annotations_off = FilePointer(irItem->annotations); dex_field_annotations.push_back(dex_item); } for (auto irItem : ir_annotations->method_annotations) { dex::MethodAnnotationsItem dex_item = {}; dex_item.method_idx = irItem->method_decl->index; dex_item.annotations_off = FilePointer(irItem->annotations); dex_method_annotations.push_back(dex_item); } for (auto irItem : ir_annotations->param_annotations) { dex::ParameterAnnotationsItem dex_item = {}; dex_item.method_idx = irItem->method_decl->index; dex_item.annotations_off = FilePointer(irItem->annotations); dex_param_annotations.push_back(dex_item); } dex::u4 class_annotations_offset = FilePointer(ir_annotations->class_annotation); // now that the annotations content is written, // we can write down the "annotations_directory_item" dex::AnnotationsDirectoryItem dex_annotations = {}; dex_annotations.class_annotations_off = class_annotations_offset; dex_annotations.fields_size = ir_annotations->field_annotations.size(); dex_annotations.methods_size = ir_annotations->method_annotations.size(); dex_annotations.parameters_size = ir_annotations->param_annotations.size(); auto& data = dex_->ann_directories; offset = data.AddItem(4); data.Push(dex_annotations); data.Push(dex_field_annotations); data.Push(dex_method_annotations); data.Push(dex_param_annotations); offset = data.AbsoluteOffset(offset); } return offset; } // "debug_info_item" dex::u4 Writer::WriteDebugInfo(const ir::DebugInfo* ir_debug_info) { SLICER_CHECK(ir_debug_info != nullptr); auto& data = dex_->debug_info; dex::u4 offset = data.AddItem(); // debug info "header" data.PushULeb128(ir_debug_info->line_start); data.PushULeb128(ir_debug_info->param_names.size()); for (auto ir_string : ir_debug_info->param_names) { data.PushULeb128(OptIndex(ir_string) + 1); } // debug info "state machine bytecodes" const dex::u1* src = ir_debug_info->data.ptr(); dex::u1 opcode = 0; while ((opcode = *src++) != dex::DBG_END_SEQUENCE) { data.Push(opcode); switch (opcode) { case dex::DBG_ADVANCE_PC: // addr_diff data.PushULeb128(dex::ReadULeb128(&src)); break; case dex::DBG_ADVANCE_LINE: // line_diff data.PushSLeb128(dex::ReadSLeb128(&src)); break; case dex::DBG_START_LOCAL: { // register_num data.PushULeb128(dex::ReadULeb128(&src)); dex::u4 name_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapStringIndex(name_index) + 1); dex::u4 type_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapTypeIndex(type_index) + 1); } break; case dex::DBG_START_LOCAL_EXTENDED: { // register_num data.PushULeb128(dex::ReadULeb128(&src)); dex::u4 name_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapStringIndex(name_index) + 1); dex::u4 type_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapTypeIndex(type_index) + 1); dex::u4 sig_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapStringIndex(sig_index) + 1); } break; case dex::DBG_END_LOCAL: case dex::DBG_RESTART_LOCAL: // register_num data.PushULeb128(dex::ReadULeb128(&src)); break; case dex::DBG_SET_FILE: { dex::u4 name_index = dex::ReadULeb128(&src) - 1; data.PushULeb128(MapStringIndex(name_index) + 1); } break; } } data.Push(dex::DBG_END_SEQUENCE); return data.AbsoluteOffset(offset); } // instruction[] array void Writer::WriteInstructions(slicer::ArrayView instructions) { SLICER_CHECK(!instructions.empty()); auto offset = dex_->code.Push(instructions); dex::u2* ptr = dex_->code.ptr(offset); dex::u2* const end = ptr + instructions.size(); // relocate the instructions while (ptr < end) { auto opcode = dex::OpcodeFromBytecode(*ptr); dex::u2* idx = &ptr[1]; size_t idx_size = 0; switch (dex::GetFormatFromOpcode(opcode)) { case dex::k20bc: case dex::k21c: case dex::k35c: case dex::k3rc: case dex::k22c: idx_size = 2; break; case dex::k31c: idx_size = 4; break; default: break; } switch (dex::GetIndexTypeFromOpcode(opcode)) { case dex::kIndexStringRef: if (idx_size == 4) { dex::u4 new_index = MapStringIndex(ReadU4(idx)); SLICER_CHECK(new_index != dex::kNoIndex); WriteU4(idx, new_index); } else { SLICER_CHECK(idx_size == 2); dex::u4 new_index = MapStringIndex(*idx); SLICER_CHECK(new_index != dex::kNoIndex); SLICER_CHECK(dex::u2(new_index) == new_index); *idx = dex::u2(new_index); } break; case dex::kIndexTypeRef: { SLICER_CHECK(idx_size == 2); dex::u4 new_index = MapTypeIndex(*idx); SLICER_CHECK(new_index != dex::kNoIndex); SLICER_CHECK(dex::u2(new_index) == new_index); *idx = dex::u2(new_index); } break; case dex::kIndexFieldRef: { SLICER_CHECK(idx_size == 2); dex::u4 new_index = MapFieldIndex(*idx); SLICER_CHECK(new_index != dex::kNoIndex); SLICER_CHECK(dex::u2(new_index) == new_index); *idx = dex::u2(new_index); } break; case dex::kIndexMethodRef: { SLICER_CHECK(idx_size == 2); dex::u4 new_index = MapMethodIndex(*idx); SLICER_CHECK(new_index != dex::kNoIndex); SLICER_CHECK(dex::u2(new_index) == new_index); *idx = dex::u2(new_index); } break; default: break; } auto isize = dex::GetWidthFromBytecode(ptr); SLICER_CHECK(isize > 0); ptr += isize; } SLICER_CHECK(ptr == end); } // "try_item[] + encoded_catch_handler_list" void Writer::WriteTryBlocks(const ir::Code* irCode) { SLICER_CHECK(!irCode->try_blocks.empty()); // use a temporary buffer to build the "encoded_catch_handler_list" slicer::Buffer handlers_list; auto original_list = irCode->catch_handlers.ptr(); auto ptr = original_list; std::map handlers_offset_map; dex::u4 handlers_count = dex::ReadULeb128(&ptr); handlers_list.PushULeb128(handlers_count); for (dex::u4 handler_index = 0; handler_index < handlers_count; ++handler_index) { // track the oldOffset/newOffset mapping handlers_offset_map[ptr - original_list] = handlers_list.size(); // parse each "encoded_catch_handler" int catch_count = dex::ReadSLeb128(&ptr); handlers_list.PushSLeb128(catch_count); for (int catch_index = 0; catch_index < std::abs(catch_count); ++catch_index) { // type_idx dex::u4 type_index = dex::ReadULeb128(&ptr); handlers_list.PushULeb128(MapTypeIndex(type_index)); // address handlers_list.PushULeb128(dex::ReadULeb128(&ptr)); } if (catch_count < 1) { // catch_all_addr handlers_list.PushULeb128(dex::ReadULeb128(&ptr)); } } handlers_list.Seal(1); // now write everything (try_item[] and encoded_catch_handler_list) auto& data = dex_->code; dex::u4 tries_offset = data.size(); data.Push(irCode->try_blocks); data.Push(handlers_list); // finally relocate the offsets to handlers for (dex::TryBlock& dex_try : slicer::ArrayView( data.ptr(tries_offset), irCode->try_blocks.size())) { dex::u2 new_Handler_offset = handlers_offset_map[dex_try.handler_off]; SLICER_CHECK(new_Handler_offset != 0); dex_try.handler_off = new_Handler_offset; } } // "code_item" dex::u4 Writer::WriteCode(const ir::Code* irCode) { SLICER_CHECK(irCode != nullptr); dex::Code dex_code = {}; dex_code.registers_size = irCode->registers; dex_code.ins_size = irCode->ins_count; dex_code.outs_size = irCode->outs_count; dex_code.tries_size = irCode->try_blocks.size(); dex_code.debug_info_off = FilePointer(irCode->debug_info); dex_code.insns_size = irCode->instructions.size(); auto& data = dex_->code; dex::u4 offset = data.AddItem(4); data.Push(&dex_code, offsetof(dex::Code, insns)); WriteInstructions(irCode->instructions); if (!irCode->try_blocks.empty()) { data.Align(4); WriteTryBlocks(irCode); } return data.AbsoluteOffset(offset); } // "encoded_field" void Writer::WriteEncodedField(const ir::EncodedField* ir_encoded_field, dex::u4* base_index) { dex::u4 index_delta = ir_encoded_field->decl->index; SLICER_CHECK(index_delta != dex::kNoIndex); if (*base_index != dex::kNoIndex) { SLICER_CHECK(index_delta > *base_index); index_delta = index_delta - *base_index; } *base_index = ir_encoded_field->decl->index; auto& data = dex_->class_data; data.PushULeb128(index_delta); data.PushULeb128(ir_encoded_field->access_flags); } // "encoded_method" void Writer::WriteEncodedMethod(const ir::EncodedMethod* ir_encoded_method, dex::u4* base_index) { dex::u4 index_delta = ir_encoded_method->decl->index; SLICER_CHECK(index_delta != dex::kNoIndex); if (*base_index != dex::kNoIndex) { SLICER_CHECK(index_delta > *base_index); index_delta = index_delta - *base_index; } *base_index = ir_encoded_method->decl->index; dex::u4 code_offset = FilePointer(ir_encoded_method->code); auto& data = dex_->class_data; data.PushULeb128(index_delta); data.PushULeb128(ir_encoded_method->access_flags); data.PushULeb128(code_offset); } // "class_data_item" dex::u4 Writer::WriteClassData(const ir::Class* ir_class) { if (ir_class->static_fields.empty() && ir_class->instance_fields.empty() && ir_class->direct_methods.empty() && ir_class->virtual_methods.empty()) { return 0; } auto& data = dex_->class_data; dex::u4 offset = data.AddItem(); data.PushULeb128(ir_class->static_fields.size()); data.PushULeb128(ir_class->instance_fields.size()); data.PushULeb128(ir_class->direct_methods.size()); data.PushULeb128(ir_class->virtual_methods.size()); dex::u4 base_index = dex::kNoIndex; for (auto ir_encoded_field : ir_class->static_fields) { WriteEncodedField(ir_encoded_field, &base_index); } base_index = dex::kNoIndex; for (auto ir_encoded_field : ir_class->instance_fields) { WriteEncodedField(ir_encoded_field, &base_index); } base_index = dex::kNoIndex; for (auto ir_encoded_method : ir_class->direct_methods) { WriteEncodedMethod(ir_encoded_method, &base_index); } base_index = dex::kNoIndex; for (auto ir_encoded_method : ir_class->virtual_methods) { WriteEncodedMethod(ir_encoded_method, &base_index); } return data.AbsoluteOffset(offset); } // "encoded_array_item" dex::u4 Writer::WriteClassStaticValues(const ir::Class* ir_class) { if (ir_class->static_init == nullptr) { return 0; } dex::u4& offset = node_offset_[ir_class->static_init]; if (offset == 0) { auto& data = dex_->encoded_arrays; offset = data.AddItem(); WriteEncodedArray(ir_class->static_init, data); offset = data.AbsoluteOffset(offset); } return offset; } // Map an index from the original .dex to the new index dex::u4 Writer::MapStringIndex(dex::u4 index) const { if (index != dex::kNoIndex) { index = dex_ir_->strings_map.at(index)->index; SLICER_CHECK(index != dex::kNoIndex); } return index; } // Map an index from the original .dex to the new index dex::u4 Writer::MapTypeIndex(dex::u4 index) const { if (index != dex::kNoIndex) { index = dex_ir_->types_map.at(index)->index; SLICER_CHECK(index != dex::kNoIndex); } return index; } // Map an index from the original .dex to the new index dex::u4 Writer::MapFieldIndex(dex::u4 index) const { if (index != dex::kNoIndex) { index = dex_ir_->fields_map.at(index)->index; SLICER_CHECK(index != dex::kNoIndex); } return index; } // Map an index from the original .dex to the new index dex::u4 Writer::MapMethodIndex(dex::u4 index) const { if (index != dex::kNoIndex) { index = dex_ir_->methods_map.at(index)->index; SLICER_CHECK(index != dex::kNoIndex); } return index; } // .dex IR node to file pointer (absolute offset) dex::u4 Writer::FilePointer(const ir::Node* ir_node) const { if (ir_node == nullptr) { return 0; } auto it = node_offset_.find(ir_node); SLICER_CHECK(it != node_offset_.end()); dex::u4 offset = it->second; SLICER_CHECK(offset > 0); return offset; } } // namespace dex