/* * Copyright (C) 2014 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_arm64.h" #include #include #include "android-base/logging.h" #include "android-base/stringprintf.h" using android::base::StringPrintf; using namespace vixl::aarch64; // NOLINT(build/namespaces) namespace art { namespace arm64 { // This enumeration should mirror the declarations in // runtime/arch/arm64/registers_arm64.h. We do not include that file to // avoid a dependency on libart. enum { TR = 19, IP0 = 16, IP1 = 17, FP = 29, LR = 30 }; void CustomDisassembler::AppendRegisterNameToOutput(const Instruction* instr, const CPURegister& reg) { USE(instr); if (reg.IsRegister() && reg.Is64Bits()) { if (reg.GetCode() == TR) { AppendToOutput("tr"); return; } else if (reg.GetCode() == LR) { AppendToOutput("lr"); return; } // Fall through. } // Print other register names as usual. Disassembler::AppendRegisterNameToOutput(instr, reg); } void CustomDisassembler::VisitLoadLiteral(const Instruction* instr) { Disassembler::VisitLoadLiteral(instr); if (!read_literals_) { return; } // Get address of literal. Bail if not within expected buffer range to // avoid trying to fetch invalid literals (we can encounter this when // interpreting raw data as instructions). void* data_address = instr->GetLiteralAddress(); if (data_address < base_address_ || data_address >= end_address_) { AppendToOutput(" (?)"); return; } // Output information on literal. Instr op = instr->Mask(LoadLiteralMask); switch (op) { case LDR_w_lit: case LDR_x_lit: case LDRSW_x_lit: { int64_t data = op == LDR_x_lit ? *reinterpret_cast(data_address) : *reinterpret_cast(data_address); AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data); break; } case LDR_s_lit: case LDR_d_lit: { double data = (op == LDR_s_lit) ? *reinterpret_cast(data_address) : *reinterpret_cast(data_address); AppendToOutput(" (%g)", data); break; } default: break; } } void CustomDisassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) { Disassembler::VisitLoadStoreUnsignedOffset(instr); if (instr->GetRn() == TR) { AppendThreadOfsetName(instr); } } void CustomDisassembler::VisitUnconditionalBranch(const Instruction* instr) { Disassembler::VisitUnconditionalBranch(instr); if (instr->Mask(UnconditionalBranchMask) == BL) { const Instruction* target = instr->GetImmPCOffsetTarget(); if (target >= base_address_ && target < end_address_ && target->Mask(LoadStoreMask) == LDR_x && target->GetRn() == TR && target->GetRt() == IP0 && target->GetNextInstruction() < end_address_ && target->GetNextInstruction()->Mask(UnconditionalBranchToRegisterMask) == BR && target->GetNextInstruction()->GetRn() == IP0) { AppendThreadOfsetName(target); } } } void CustomDisassembler::AppendThreadOfsetName(const vixl::aarch64::Instruction* instr) { int64_t offset = instr->GetImmLSUnsigned() << instr->GetSizeLS(); std::ostringstream tmp_stream; options_->thread_offset_name_function_(tmp_stream, static_cast(offset)); AppendToOutput(" ; %s", tmp_stream.str().c_str()); } size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) { const Instruction* instr = reinterpret_cast(begin); decoder.Decode(instr); os << FormatInstructionPointer(begin) << StringPrintf(": %08x\t%s\n", instr->GetInstructionBits(), disasm.GetOutput()); return kInstructionSize; } void DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { for (const uint8_t* cur = begin; cur < end; cur += kInstructionSize) { Dump(os, cur); } } } // namespace arm64 } // namespace art