/* * 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/common.h" #include "slicer/code_ir.h" #include "slicer/dex_bytecode.h" #include "slicer/dex_format.h" #include "slicer/dex_ir.h" #include "slicer/dex_leb128.h" #include "slicer/bytecode_encoder.h" #include "slicer/debuginfo_encoder.h" #include "slicer/tryblocks_encoder.h" #include #include #include #include #include #include namespace lir { void CodeIr::Assemble() { auto ir_code = ir_method->code; SLICER_CHECK(ir_code != nullptr); // new .dex bytecode // // NOTE: this must be done before the debug information and // try/catch blocks since here is where we update the final offsets // BytecodeEncoder bytecode_encoder(instructions); bytecode_encoder.Encode(ir_code, dex_ir); // debug information if (ir_code->debug_info != nullptr) { DebugInfoEncoder dbginfo_encoder(instructions); dbginfo_encoder.Encode(ir_method, dex_ir); } // try/catch blocks TryBlocksEncoder try_blocks_encoder(instructions); try_blocks_encoder.Encode(ir_code, dex_ir); } void CodeIr::DissasembleTryBlocks(const ir::Code* ir_code) { int nextTryBlockId = 1; for (const auto& tryBlock : ir_code->try_blocks) { auto try_block_begin = Alloc(); try_block_begin->id = nextTryBlockId++; try_block_begin->offset = tryBlock.start_addr; auto try_block_end = Alloc(); try_block_end->try_begin = try_block_begin; try_block_end->offset = tryBlock.start_addr + tryBlock.insn_count; // parse the catch handlers const dex::u1* ptr = ir_code->catch_handlers.ptr() + tryBlock.handler_off; int catchCount = dex::ReadSLeb128(&ptr); for (int catchIndex = 0; catchIndex < std::abs(catchCount); ++catchIndex) { CatchHandler handler = {}; // type dex::u4 type_index = dex::ReadULeb128(&ptr); handler.ir_type = dex_ir->types_map[type_index]; SLICER_CHECK(handler.ir_type != nullptr); // address dex::u4 address = dex::ReadULeb128(&ptr); handler.label = GetLabel(address); try_block_end->handlers.push_back(handler); } // catch_all handler? // // NOTE: this is used to generate code for the "finally" blocks // (see Java Virtual Machine Specification - 3.13 "Compiling finally") // if (catchCount < 1) { dex::u4 address = dex::ReadULeb128(&ptr); try_block_end->catch_all = GetLabel(address); } // we should have at least one handler SLICER_CHECK(!try_block_end->handlers.empty() || try_block_end->catch_all != nullptr); try_begins_.push_back(try_block_begin); try_ends_.push_back(try_block_end); } } void CodeIr::DissasembleDebugInfo(const ir::DebugInfo* ir_debug_info) { if (ir_debug_info == nullptr) { return; } // debug info state machine registers dex::u4 address = 0; int line = ir_debug_info->line_start; ir::String* source_file = ir_method->decl->parent->class_def->source_file; // header if (!ir_debug_info->param_names.empty()) { auto dbg_header = Alloc(); dbg_header->param_names = ir_debug_info->param_names; dbg_header->offset = 0; dbg_annotations_.push_back(dbg_header); } // initial source file { auto annotation = Alloc(dex::DBG_SET_FILE); annotation->offset = 0; annotation->operands.push_back(Alloc( source_file, source_file ? source_file->orig_index : dex::kNoIndex)); dbg_annotations_.push_back(annotation); } // initial line number - redundant? { auto annotation = Alloc(dex::DBG_ADVANCE_LINE); annotation->offset = 0; annotation->operands.push_back(Alloc(line)); dbg_annotations_.push_back(annotation); } // debug info annotations const dex::u1* ptr = ir_debug_info->data.ptr(); dex::u1 opcode = 0; while ((opcode = *ptr++) != dex::DBG_END_SEQUENCE) { DbgInfoAnnotation* annotation = nullptr; switch (opcode) { case dex::DBG_ADVANCE_PC: // addr_diff address += dex::ReadULeb128(&ptr); break; case dex::DBG_ADVANCE_LINE: // line_diff line += dex::ReadSLeb128(&ptr); SLICER_WEAK_CHECK(line > 0); break; case dex::DBG_START_LOCAL: { annotation = Alloc(opcode); // register_num annotation->operands.push_back(Alloc(dex::ReadULeb128(&ptr))); // name dex::u4 name_index = dex::ReadULeb128(&ptr) - 1; annotation->operands.push_back(GetString(name_index)); // type dex::u4 type_index = dex::ReadULeb128(&ptr) - 1; annotation->operands.push_back(GetType(type_index)); } break; case dex::DBG_START_LOCAL_EXTENDED: { annotation = Alloc(opcode); // register_num annotation->operands.push_back(Alloc(dex::ReadULeb128(&ptr))); // name dex::u4 name_index = dex::ReadULeb128(&ptr) - 1; annotation->operands.push_back(GetString(name_index)); // type dex::u4 type_index = dex::ReadULeb128(&ptr) - 1; annotation->operands.push_back(GetType(type_index)); // signature dex::u4 sig_index = dex::ReadULeb128(&ptr) - 1; annotation->operands.push_back(GetString(sig_index)); } break; case dex::DBG_END_LOCAL: case dex::DBG_RESTART_LOCAL: annotation = Alloc(opcode); // register_num annotation->operands.push_back(Alloc(dex::ReadULeb128(&ptr))); break; case dex::DBG_SET_PROLOGUE_END: annotation = Alloc(opcode); break; case dex::DBG_SET_EPILOGUE_BEGIN: annotation = Alloc(opcode); break; case dex::DBG_SET_FILE: { annotation = Alloc(opcode); // source file name dex::u4 name_index = dex::ReadULeb128(&ptr) - 1; source_file = (name_index == dex::kNoIndex) ? nullptr : dex_ir->strings_map[name_index]; annotation->operands.push_back(Alloc(source_file, name_index)); } break; default: { int adjusted_opcode = opcode - dex::DBG_FIRST_SPECIAL; line += dex::DBG_LINE_BASE + (adjusted_opcode % dex::DBG_LINE_RANGE); address += (adjusted_opcode / dex::DBG_LINE_RANGE); SLICER_WEAK_CHECK(line > 0); annotation = Alloc(dex::DBG_ADVANCE_LINE); annotation->operands.push_back(Alloc(line)); } break; } if (annotation != nullptr) { annotation->offset = address; dbg_annotations_.push_back(annotation); } } } void CodeIr::DissasembleBytecode(const ir::Code* ir_code) { const dex::u2* begin = ir_code->instructions.begin(); const dex::u2* end = ir_code->instructions.end(); const dex::u2* ptr = begin; while (ptr < end) { auto isize = dex::GetWidthFromBytecode(ptr); SLICER_CHECK(isize > 0); dex::u4 offset = ptr - begin; Instruction* instr = nullptr; switch (*ptr) { case dex::kPackedSwitchSignature: instr = DecodePackedSwitch(ptr, offset); break; case dex::kSparseSwitchSignature: instr = DecodeSparseSwitch(ptr, offset); break; case dex::kArrayDataSignature: instr = DecodeArrayData(ptr, offset); break; default: instr = DecodeBytecode(ptr, offset); break; } instr->offset = offset; instructions.push_back(instr); ptr += isize; } SLICER_CHECK(ptr == end); } void CodeIr::FixupSwitches() { const dex::u2* begin = ir_method->code->instructions.begin(); // packed switches for (auto& fixup : packed_switches_) { FixupPackedSwitch(fixup.second.instr, fixup.second.base_offset, begin + fixup.first); } // sparse switches for (auto& fixup : sparse_switches_) { FixupSparseSwitch(fixup.second.instr, fixup.second.base_offset, begin + fixup.first); } } // merge a set of extra instructions into the instruction list template static void MergeInstructions(I_LIST& instructions, const E_LIST& extra) { // the extra instructins must be sorted by offset SLICER_CHECK(std::is_sorted(extra.begin(), extra.end(), [](const Instruction* a, const Instruction* b) { return a->offset < b->offset; })); auto instrIt = instructions.begin(); auto extraIt = extra.begin(); while (extraIt != extra.end()) { if (instrIt == instructions.end() || (*extraIt)->offset == (*instrIt)->offset) { instructions.insert(instrIt, *extraIt); ++extraIt; } else { ++instrIt; } } } void CodeIr::Dissasemble() { nodes_.clear(); labels_.clear(); try_begins_.clear(); try_ends_.clear(); dbg_annotations_.clear(); packed_switches_.clear(); sparse_switches_.clear(); auto ir_code = ir_method->code; if (ir_code == nullptr) { return; } // decode the .dex bytecodes DissasembleBytecode(ir_code); // try/catch blocks DissasembleTryBlocks(ir_code); // debug information DissasembleDebugInfo(ir_code->debug_info); // fixup switches FixupSwitches(); // assign label ids std::vector tmp_labels; int nextLabelId = 1; for (auto& label : labels_) { label.second->id = nextLabelId++; tmp_labels.push_back(label.second); } // merge the labels into the instructions stream MergeInstructions(instructions, dbg_annotations_); MergeInstructions(instructions, try_begins_); MergeInstructions(instructions, tmp_labels); MergeInstructions(instructions, try_ends_); } PackedSwitchPayload* CodeIr::DecodePackedSwitch(const dex::u2* /*ptr*/, dex::u4 offset) { // actual decoding is delayed to FixupPackedSwitch() // (since the label offsets are relative to the referring // instruction, not the switch data) SLICER_CHECK(offset % 2 == 0); auto& instr = packed_switches_[offset].instr; SLICER_CHECK(instr == nullptr); instr = Alloc(); return instr; } void CodeIr::FixupPackedSwitch(PackedSwitchPayload* instr, dex::u4 base_offset, const dex::u2* ptr) { SLICER_CHECK(instr->targets.empty()); auto dex_packed_switch = reinterpret_cast(ptr); SLICER_CHECK(dex_packed_switch->ident == dex::kPackedSwitchSignature); instr->first_key = dex_packed_switch->first_key; for (dex::u2 i = 0; i < dex_packed_switch->size; ++i) { instr->targets.push_back( GetLabel(base_offset + dex_packed_switch->targets[i])); } } SparseSwitchPayload* CodeIr::DecodeSparseSwitch(const dex::u2* /*ptr*/, dex::u4 offset) { // actual decoding is delayed to FixupSparseSwitch() // (since the label offsets are relative to the referring // instruction, not the switch data) SLICER_CHECK(offset % 2 == 0); auto& instr = sparse_switches_[offset].instr; SLICER_CHECK(instr == nullptr); instr = Alloc(); return instr; } void CodeIr::FixupSparseSwitch(SparseSwitchPayload* instr, dex::u4 base_offset, const dex::u2* ptr) { SLICER_CHECK(instr->switch_cases.empty()); auto dex_sparse_switch = reinterpret_cast(ptr); SLICER_CHECK(dex_sparse_switch->ident == dex::kSparseSwitchSignature); auto& data = dex_sparse_switch->data; auto& size = dex_sparse_switch->size; for (dex::u2 i = 0; i < size; ++i) { SparseSwitchPayload::SwitchCase switch_case = {}; switch_case.key = data[i]; switch_case.target = GetLabel(base_offset + data[i + size]); instr->switch_cases.push_back(switch_case); } } ArrayData* CodeIr::DecodeArrayData(const dex::u2* ptr, dex::u4 offset) { auto dex_array_data = reinterpret_cast(ptr); SLICER_CHECK(dex_array_data->ident == dex::kArrayDataSignature); SLICER_CHECK(offset % 2 == 0); auto instr = Alloc(); instr->data = slicer::MemView(ptr, dex::GetWidthFromBytecode(ptr) * 2); return instr; } Operand* CodeIr::GetRegA(const dex::Instruction& dex_instr) { auto verify_flags = dex::GetVerifyFlagsFromOpcode(dex_instr.opcode); if ((verify_flags & dex::kVerifyRegAWide) != 0) { return Alloc(dex_instr.vA); } else { return Alloc(dex_instr.vA); } } Operand* CodeIr::GetRegB(const dex::Instruction& dex_instr) { auto verify_flags = dex::GetVerifyFlagsFromOpcode(dex_instr.opcode); if ((verify_flags & dex::kVerifyRegBWide) != 0) { return Alloc(dex_instr.vB); } else { return Alloc(dex_instr.vB); } } Operand* CodeIr::GetRegC(const dex::Instruction& dex_instr) { auto verify_flags = dex::GetVerifyFlagsFromOpcode(dex_instr.opcode); if ((verify_flags & dex::kVerifyRegCWide) != 0) { return Alloc(dex_instr.vC); } else { return Alloc(dex_instr.vC); } } Bytecode* CodeIr::DecodeBytecode(const dex::u2* ptr, dex::u4 offset) { auto dex_instr = dex::DecodeInstruction(ptr); auto instr = Alloc(); instr->opcode = dex_instr.opcode; auto index_type = dex::GetIndexTypeFromOpcode(dex_instr.opcode); switch (dex::GetFormatFromOpcode(dex_instr.opcode)) { case dex::k10x: // op break; case dex::k12x: // op vA, vB case dex::k22x: // op vAA, vBBBB case dex::k32x: // op vAAAA, vBBBB instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetRegB(dex_instr)); break; case dex::k11n: // op vA, #+B case dex::k21s: // op vAA, #+BBBB case dex::k31i: // op vAA, #+BBBBBBBB instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(Alloc(dex_instr.vB)); break; case dex::k11x: // op vAA instr->operands.push_back(GetRegA(dex_instr)); break; case dex::k10t: // op +AA case dex::k20t: // op +AAAA case dex::k30t: // op +AAAAAAAA { auto label = GetLabel(offset + dex::s4(dex_instr.vA)); instr->operands.push_back(Alloc(label)); } break; case dex::k21t: // op vAA, +BBBB case dex::k31t: // op vAA, +BBBBBBBB { dex::u4 targetOffset = offset + dex::s4(dex_instr.vB); instr->operands.push_back(GetRegA(dex_instr)); auto label = GetLabel(targetOffset); instr->operands.push_back(Alloc(label)); if (dex_instr.opcode == dex::OP_PACKED_SWITCH) { label->aligned = true; dex::u4& base_offset = packed_switches_[targetOffset].base_offset; SLICER_CHECK(base_offset == kInvalidOffset); base_offset = offset; } else if (dex_instr.opcode == dex::OP_SPARSE_SWITCH) { label->aligned = true; dex::u4& base_offset = sparse_switches_[targetOffset].base_offset; SLICER_CHECK(base_offset == kInvalidOffset); base_offset = offset; } else if (dex_instr.opcode == dex::OP_FILL_ARRAY_DATA) { label->aligned = true; } } break; case dex::k23x: // op vAA, vBB, vCC instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetRegB(dex_instr)); instr->operands.push_back(GetRegC(dex_instr)); break; case dex::k22t: // op vA, vB, +CCCC { instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetRegB(dex_instr)); auto label = GetLabel(offset + dex::s4(dex_instr.vC)); instr->operands.push_back(Alloc(label)); } break; case dex::k22b: // op vAA, vBB, #+CC case dex::k22s: // op vA, vB, #+CCCC instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetRegB(dex_instr)); instr->operands.push_back(Alloc(dex_instr.vC)); break; case dex::k22c: // op vA, vB, thing@CCCC instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetRegB(dex_instr)); instr->operands.push_back(GetIndexedOperand(index_type, dex_instr.vC)); break; case dex::k21c: // op vAA, thing@BBBB case dex::k31c: // op vAA, string@BBBBBBBB instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(GetIndexedOperand(index_type, dex_instr.vB)); break; case dex::k35c: // op {vC,vD,vE,vF,vG}, thing@BBBB { SLICER_CHECK(dex_instr.vA <= 5); auto vreg_list = Alloc(); for (dex::u4 i = 0; i < dex_instr.vA; ++i) { vreg_list->registers.push_back(dex_instr.arg[i]); } instr->operands.push_back(vreg_list); instr->operands.push_back(GetIndexedOperand(index_type, dex_instr.vB)); } break; case dex::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB { auto vreg_range = Alloc(dex_instr.vC, dex_instr.vA); instr->operands.push_back(vreg_range); instr->operands.push_back(GetIndexedOperand(index_type, dex_instr.vB)); } break; case dex::k21h: // op vAA, #+BBBB0000[00000000] switch (dex_instr.opcode) { case dex::OP_CONST_HIGH16: instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(Alloc(dex_instr.vB << 16)); break; case dex::OP_CONST_WIDE_HIGH16: instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(Alloc(dex::u8(dex_instr.vB) << 48)); break; default: SLICER_FATAL("Unexpected opcode 0x%02x", dex_instr.opcode); } break; case dex::k51l: // op vAA, #+BBBBBBBBBBBBBBBB instr->operands.push_back(GetRegA(dex_instr)); instr->operands.push_back(Alloc(dex_instr.vB_wide)); break; default: SLICER_FATAL("Unexpected bytecode format (opcode 0x%02x)", dex_instr.opcode); } return instr; } // Get a indexed object (string, field, ...) // (index must be valid != kNoIndex) IndexedOperand* CodeIr::GetIndexedOperand(dex::InstructionIndexType index_type, dex::u4 index) { SLICER_CHECK(index != dex::kNoIndex); switch (index_type) { case dex::kIndexStringRef: return Alloc(dex_ir->strings_map[index], index); case dex::kIndexTypeRef: return Alloc(dex_ir->types_map[index], index); case dex::kIndexFieldRef: return Alloc(dex_ir->fields_map[index], index); case dex::kIndexMethodRef: return Alloc(dex_ir->methods_map[index], index); default: SLICER_FATAL("Unexpected index type 0x%02x", index_type); } } // Get a type based on its index (potentially kNoIndex) Type* CodeIr::GetType(dex::u4 index) { auto ir_type = (index == dex::kNoIndex) ? nullptr : dex_ir->types_map[index]; return Alloc(ir_type, index); } // Get a string based on its index (potentially kNoIndex) String* CodeIr::GetString(dex::u4 index) { auto ir_string = (index == dex::kNoIndex) ? nullptr : dex_ir->strings_map[index]; return Alloc(ir_string, index); } // Get en existing, or new label for a particular offset Label* CodeIr::GetLabel(dex::u4 offset) { auto& p = labels_[offset]; if (p == nullptr) { p = Alloc