149 lines
4.5 KiB
C++
149 lines
4.5 KiB
C++
/*
|
|
* 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
|