blob: 0d51cfdc1ab605d7ced52691349b651902aa4f4f [file] [log] [blame] [edit]
/*
* 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 <inttypes.h>
#include <sstream>
#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<void*>();
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<int64_t*>(data_address)
: *reinterpret_cast<int32_t*>(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<float*>(data_address)
: *reinterpret_cast<double*>(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<uint32_t>(offset));
AppendToOutput(" ; %s", tmp_stream.str().c_str());
}
size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
const Instruction* instr = reinterpret_cast<const Instruction*>(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