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