1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "disassembler_arm64.h"
18
19 #include <inttypes.h>
20
21 #include <sstream>
22
23 #include "android-base/logging.h"
24 #include "android-base/stringprintf.h"
25
26 using android::base::StringPrintf;
27
28 using namespace vixl::aarch64; // NOLINT(build/namespaces)
29
30 namespace art {
31 namespace arm64 {
32
33 // This enumeration should mirror the declarations in
34 // runtime/arch/arm64/registers_arm64.h. We do not include that file to
35 // avoid a dependency on libart.
36 enum {
37 TR = 19,
38 IP0 = 16,
39 IP1 = 17,
40 FP = 29,
41 LR = 30
42 };
43
AppendRegisterNameToOutput(const Instruction * instr,const CPURegister & reg)44 void CustomDisassembler::AppendRegisterNameToOutput(const Instruction* instr,
45 const CPURegister& reg) {
46 USE(instr);
47 if (reg.IsRegister() && reg.Is64Bits()) {
48 if (reg.GetCode() == TR) {
49 AppendToOutput("tr");
50 return;
51 } else if (reg.GetCode() == LR) {
52 AppendToOutput("lr");
53 return;
54 }
55 // Fall through.
56 }
57 // Print other register names as usual.
58 Disassembler::AppendRegisterNameToOutput(instr, reg);
59 }
60
VisitLoadLiteral(const Instruction * instr)61 void CustomDisassembler::VisitLoadLiteral(const Instruction* instr) {
62 Disassembler::VisitLoadLiteral(instr);
63
64 if (!read_literals_) {
65 return;
66 }
67
68 // Get address of literal. Bail if not within expected buffer range to
69 // avoid trying to fetch invalid literals (we can encounter this when
70 // interpreting raw data as instructions).
71 void* data_address = instr->GetLiteralAddress<void*>();
72 if (data_address < base_address_ || data_address >= end_address_) {
73 AppendToOutput(" (?)");
74 return;
75 }
76
77 // Output information on literal.
78 Instr op = instr->Mask(LoadLiteralMask);
79 switch (op) {
80 case LDR_w_lit:
81 case LDR_x_lit:
82 case LDRSW_x_lit: {
83 int64_t data = op == LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
84 : *reinterpret_cast<int32_t*>(data_address);
85 AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data);
86 break;
87 }
88 case LDR_s_lit:
89 case LDR_d_lit: {
90 double data = (op == LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
91 : *reinterpret_cast<double*>(data_address);
92 AppendToOutput(" (%g)", data);
93 break;
94 }
95 default:
96 break;
97 }
98 }
99
VisitLoadStoreUnsignedOffset(const Instruction * instr)100 void CustomDisassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) {
101 Disassembler::VisitLoadStoreUnsignedOffset(instr);
102
103 if (instr->GetRn() == TR) {
104 AppendThreadOfsetName(instr);
105 }
106 }
107
VisitUnconditionalBranch(const Instruction * instr)108 void CustomDisassembler::VisitUnconditionalBranch(const Instruction* instr) {
109 Disassembler::VisitUnconditionalBranch(instr);
110
111 if (instr->Mask(UnconditionalBranchMask) == BL) {
112 const Instruction* target = instr->GetImmPCOffsetTarget();
113 if (target >= base_address_ &&
114 target < end_address_ &&
115 target->Mask(LoadStoreMask) == LDR_x &&
116 target->GetRn() == TR &&
117 target->GetRt() == IP0 &&
118 target->GetNextInstruction() < end_address_ &&
119 target->GetNextInstruction()->Mask(UnconditionalBranchToRegisterMask) == BR &&
120 target->GetNextInstruction()->GetRn() == IP0) {
121 AppendThreadOfsetName(target);
122 }
123 }
124 }
125
AppendThreadOfsetName(const vixl::aarch64::Instruction * instr)126 void CustomDisassembler::AppendThreadOfsetName(const vixl::aarch64::Instruction* instr) {
127 int64_t offset = instr->GetImmLSUnsigned() << instr->GetSizeLS();
128 std::ostringstream tmp_stream;
129 options_->thread_offset_name_function_(tmp_stream, static_cast<uint32_t>(offset));
130 AppendToOutput(" ; %s", tmp_stream.str().c_str());
131 }
132
Dump(std::ostream & os,const uint8_t * begin)133 size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
134 const Instruction* instr = reinterpret_cast<const Instruction*>(begin);
135 decoder.Decode(instr);
136 os << FormatInstructionPointer(begin)
137 << StringPrintf(": %08x\t%s\n", instr->GetInstructionBits(), disasm.GetOutput());
138 return kInstructionSize;
139 }
140
Dump(std::ostream & os,const uint8_t * begin,const uint8_t * end)141 void DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
142 for (const uint8_t* cur = begin; cur < end; cur += kInstructionSize) {
143 Dump(os, cur);
144 }
145 }
146
147 } // namespace arm64
148 } // namespace art
149