| /* |
| * 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 "disassembler.h" |
| |
| #include <stdio.h> |
| #include <cinttypes> |
| #include <cmath> |
| #include <sstream> |
| |
| // Builds a human readable method declaration, not including the name, ex: |
| // "(android.content.Context, android.content.pm.ActivityInfo) : java.lang.String" |
| static std::string MethodDeclaration(const ir::Proto* proto) { |
| std::stringstream ss; |
| ss << "("; |
| if (proto->param_types != nullptr) { |
| bool first = true; |
| for (auto type : proto->param_types->types) { |
| ss << (first ? "" : ", ") << type->Decl(); |
| first = false; |
| } |
| } |
| ss << "):"; |
| ss << proto->return_type->Decl(); |
| return ss.str(); |
| } |
| |
| void PrintCodeIrVisitor::StartInstruction(const lir::Instruction* instr) { |
| if (cfg_ == nullptr || current_block_index_ >= cfg_->basic_blocks.size()) { |
| return; |
| } |
| const lir::BasicBlock& current_block = cfg_->basic_blocks[current_block_index_]; |
| if (instr == current_block.region.first) { |
| printf("............................. begin block %d .............................\n", current_block.id); |
| } |
| } |
| |
| void PrintCodeIrVisitor::EndInstruction(const lir::Instruction* instr) { |
| if (cfg_ == nullptr || current_block_index_ >= cfg_->basic_blocks.size()) { |
| return; |
| } |
| const lir::BasicBlock& current_block = cfg_->basic_blocks[current_block_index_]; |
| if (instr == current_block.region.last) { |
| printf(".............................. end block %d ..............................\n", current_block.id); |
| ++current_block_index_; |
| } |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Bytecode* bytecode) { |
| StartInstruction(bytecode); |
| printf("\t%5u| %s", bytecode->offset, dex::GetOpcodeName(bytecode->opcode)); |
| bool first = true; |
| for (auto op : bytecode->operands) { |
| printf(first ? " " : ", "); |
| op->Accept(this); |
| first = false; |
| } |
| printf("\n"); |
| EndInstruction(bytecode); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::PackedSwitchPayload* packed_switch) { |
| StartInstruction(packed_switch); |
| printf("\t%5u| packed-switch-payload\n", packed_switch->offset); |
| int key = packed_switch->first_key; |
| for (auto target : packed_switch->targets) { |
| printf("\t\t%5d: ", key++); |
| printf("Label_%d", target->id); |
| printf("\n"); |
| } |
| EndInstruction(packed_switch); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::SparseSwitchPayload* sparse_switch) { |
| StartInstruction(sparse_switch); |
| printf("\t%5u| sparse-switch-payload\n", sparse_switch->offset); |
| for (auto& switchCase : sparse_switch->switch_cases) { |
| printf("\t\t%5d: ", switchCase.key); |
| printf("Label_%d", switchCase.target->id); |
| printf("\n"); |
| } |
| EndInstruction(sparse_switch); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::ArrayData* array_data) { |
| StartInstruction(array_data); |
| printf("\t%5u| fill-array-data-payload\n", array_data->offset); |
| EndInstruction(array_data); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::CodeLocation* target) { |
| printf("Label_%d", target->label->id); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Const32* const32) { |
| printf("#%+d (0x%08x | ", const32->u.s4_value, const32->u.u4_value); |
| if (std::isnan(const32->u.float_value)) { |
| printf("NaN)"); |
| } else { |
| printf("%#.6g)", const32->u.float_value); |
| } |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Const64* const64) { |
| printf("#%+" PRId64 " (0x%016" PRIx64 " | ", const64->u.s8_value, const64->u.u8_value); |
| if (std::isnan(const64->u.double_value)) { |
| printf("NaN)"); |
| } else { |
| printf("%#.6g)", const64->u.double_value); |
| } |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::VReg* vreg) { |
| printf("v%d", vreg->reg); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::VRegPair* vreg_pair) { |
| printf("v%d:v%d", vreg_pair->base_reg, vreg_pair->base_reg + 1); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::VRegList* vreg_list) { |
| bool first = true; |
| printf("{"); |
| for (auto reg : vreg_list->registers) { |
| printf("%sv%d", (first ? "" : ","), reg); |
| first = false; |
| } |
| printf("}"); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::VRegRange* vreg_range) { |
| if (vreg_range->count == 0) { |
| printf("{}"); |
| } else { |
| printf("{v%d..v%d}", vreg_range->base_reg, |
| vreg_range->base_reg + vreg_range->count - 1); |
| } |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::String* string) { |
| if (string->ir_string == nullptr) { |
| printf("<null>"); |
| return true; |
| } |
| auto ir_string = string->ir_string; |
| printf("\""); |
| for (const char* p = ir_string->c_str(); *p != '\0'; ++p) { |
| if (::isprint(*p)) { |
| printf("%c", *p); |
| } else { |
| switch (*p) { |
| case '\'': printf("\\'"); break; |
| case '\"': printf("\\\""); break; |
| case '\?': printf("\\?"); break; |
| case '\\': printf("\\\\"); break; |
| case '\a': printf("\\a"); break; |
| case '\b': printf("\\b"); break; |
| case '\f': printf("\\f"); break; |
| case '\n': printf("\\n"); break; |
| case '\r': printf("\\r"); break; |
| case '\t': printf("\\t"); break; |
| case '\v': printf("\\v"); break; |
| default: |
| printf("\\x%02x", *p); |
| break; |
| } |
| } |
| } |
| printf("\""); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Type* type) { |
| SLICER_CHECK_NE(type->index, dex::kNoIndex); |
| auto ir_type = type->ir_type; |
| printf("%s", ir_type->Decl().c_str()); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Field* field) { |
| SLICER_CHECK_NE(field->index, dex::kNoIndex); |
| auto ir_field = field->ir_field; |
| printf("%s.%s", ir_field->parent->Decl().c_str(), ir_field->name->c_str()); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Method* method) { |
| SLICER_CHECK_NE(method->index, dex::kNoIndex); |
| auto ir_method = method->ir_method; |
| printf("%s.%s%s", |
| ir_method->parent->Decl().c_str(), |
| ir_method->name->c_str(), |
| MethodDeclaration(ir_method->prototype).c_str()); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Proto* proto) { |
| SLICER_CHECK_NE(proto->index, dex::kNoIndex); |
| auto ir_proto = proto->ir_proto; |
| printf("%s", MethodDeclaration(ir_proto).c_str()); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::LineNumber* line_number) { |
| printf("%d", line_number->line); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::Label* label) { |
| StartInstruction(label); |
| printf("Label_%d:%s\n", label->id, (label->aligned ? " <aligned>" : "")); |
| EndInstruction(label); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::TryBlockBegin* try_begin) { |
| StartInstruction(try_begin); |
| printf("\t.try_begin_%d\n", try_begin->id); |
| EndInstruction(try_begin); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::TryBlockEnd* try_end) { |
| StartInstruction(try_end); |
| printf("\t.try_end_%d\n", try_end->try_begin->id); |
| for (const auto& handler : try_end->handlers) { |
| printf("\t catch(%s) : Label_%d\n", handler.ir_type->Decl().c_str(), |
| handler.label->id); |
| } |
| if (try_end->catch_all != nullptr) { |
| printf("\t catch(...) : Label_%d\n", try_end->catch_all->id); |
| } |
| EndInstruction(try_end); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::DbgInfoHeader* dbg_header) { |
| StartInstruction(dbg_header); |
| printf("\t.params"); |
| bool first = true; |
| for (auto paramName : dbg_header->param_names) { |
| printf(first ? " " : ", "); |
| printf("\"%s\"", paramName ? paramName->c_str() : "?"); |
| first = false; |
| } |
| printf("\n"); |
| EndInstruction(dbg_header); |
| return true; |
| } |
| |
| bool PrintCodeIrVisitor::Visit(lir::DbgInfoAnnotation* annotation) { |
| StartInstruction(annotation); |
| const char* name = ".dbg_???"; |
| switch (annotation->dbg_opcode) { |
| case dex::DBG_START_LOCAL: |
| name = ".local"; |
| break; |
| case dex::DBG_START_LOCAL_EXTENDED: |
| name = ".local_ex"; |
| break; |
| case dex::DBG_END_LOCAL: |
| name = ".end_local"; |
| break; |
| case dex::DBG_RESTART_LOCAL: |
| name = ".restart_local"; |
| break; |
| case dex::DBG_SET_PROLOGUE_END: |
| name = ".prologue_end"; |
| break; |
| case dex::DBG_SET_EPILOGUE_BEGIN: |
| name = ".epilogue_begin"; |
| break; |
| case dex::DBG_ADVANCE_LINE: |
| name = ".line"; |
| break; |
| case dex::DBG_SET_FILE: |
| name = ".src"; |
| break; |
| } |
| printf("\t%s", name); |
| |
| bool first = true; |
| for (auto op : annotation->operands) { |
| printf(first ? " " : ", "); |
| op->Accept(this); |
| first = false; |
| } |
| |
| printf("\n"); |
| EndInstruction(annotation); |
| return true; |
| } |
| |
| void DexDisassembler::DumpAllMethods() const { |
| for (auto& ir_method : dex_ir_->encoded_methods) { |
| DumpMethod(ir_method.get()); |
| } |
| } |
| |
| void DexDisassembler::DumpMethod(ir::EncodedMethod* ir_method) const { |
| printf("\nmethod %s.%s%s\n{\n", |
| ir_method->decl->parent->Decl().c_str(), |
| ir_method->decl->name->c_str(), |
| MethodDeclaration(ir_method->decl->prototype).c_str()); |
| Disassemble(ir_method); |
| printf("}\n"); |
| } |
| |
| void DexDisassembler::Disassemble(ir::EncodedMethod* ir_method) const { |
| lir::CodeIr code_ir(ir_method, dex_ir_); |
| std::unique_ptr<lir::ControlFlowGraph> cfg; |
| switch (cfg_type_) { |
| case CfgType::Compact: |
| cfg.reset(new lir::ControlFlowGraph(&code_ir, false)); |
| break; |
| case CfgType::Verbose: |
| cfg.reset(new lir::ControlFlowGraph(&code_ir, true)); |
| break; |
| default: |
| break; |
| } |
| PrintCodeIrVisitor visitor(dex_ir_, cfg.get()); |
| code_ir.Accept(&visitor); |
| } |