1 /* 2 * Copyright (C) 2012 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_arm.h" 18 19 #include <memory> 20 #include <string> 21 22 #include "android-base/logging.h" 23 24 #include "arch/arm/registers_arm.h" 25 #include "base/bit_utils.h" 26 27 #pragma GCC diagnostic push 28 #pragma GCC diagnostic ignored "-Wshadow" 29 #include "aarch32/disasm-aarch32.h" 30 #include "aarch32/instructions-aarch32.h" 31 #pragma GCC diagnostic pop 32 33 namespace art { 34 namespace arm { 35 36 using vixl::aarch32::MemOperand; 37 using vixl::aarch32::PrintDisassembler; 38 using vixl::aarch32::pc; 39 40 static const vixl::aarch32::Register tr(TR); 41 42 class DisassemblerArm::CustomDisassembler final : public PrintDisassembler { 43 class CustomDisassemblerStream final : public DisassemblerStream { 44 public: 45 CustomDisassemblerStream(std::ostream& os, 46 const CustomDisassembler* disasm, 47 const DisassemblerOptions* options) 48 : DisassemblerStream(os), disasm_(disasm), options_(options) {} 49 50 DisassemblerStream& operator<<(const PrintLabel& label) override { 51 const LocationType type = label.GetLocationType(); 52 53 switch (type) { 54 case kLoadByteLocation: 55 case kLoadHalfWordLocation: 56 case kLoadWordLocation: 57 case kLoadDoubleWordLocation: 58 case kLoadSignedByteLocation: 59 case kLoadSignedHalfWordLocation: 60 case kLoadSinglePrecisionLocation: 61 case kLoadDoublePrecisionLocation: 62 case kVld1Location: 63 case kVld2Location: 64 case kVld3Location: 65 case kVld4Location: { 66 const int32_t offset = label.GetImmediate(); 67 os() << "[pc, #" << offset << "]"; 68 PrintLiteral(type, offset); 69 return *this; 70 } 71 default: 72 return DisassemblerStream::operator<<(label); 73 } 74 } 75 76 DisassemblerStream& operator<<(vixl::aarch32::Register reg) override { 77 if (reg.Is(tr)) { 78 os() << "tr"; 79 return *this; 80 } else { 81 return DisassemblerStream::operator<<(reg); 82 } 83 } 84 85 DisassemblerStream& operator<<(const MemOperand& operand) override { 86 // VIXL must use a PrintLabel object whenever the base register is PC; 87 // the following check verifies this invariant, and guards against bugs. 88 DCHECK(!operand.GetBaseRegister().Is(pc)); 89 DisassemblerStream::operator<<(operand); 90 91 if (operand.GetBaseRegister().Is(tr) && operand.IsImmediate()) { 92 os() << " ; "; 93 options_->thread_offset_name_function_(os(), operand.GetOffsetImmediate()); 94 } 95 96 return *this; 97 } 98 99 DisassemblerStream& operator<<(const vixl::aarch32::AlignedMemOperand& operand) override { 100 // VIXL must use a PrintLabel object whenever the base register is PC; 101 // the following check verifies this invariant, and guards against bugs. 102 DCHECK(!operand.GetBaseRegister().Is(pc)); 103 return DisassemblerStream::operator<<(operand); 104 } 105 106 private: 107 void PrintLiteral(LocationType type, int32_t offset); 108 109 const CustomDisassembler* disasm_; 110 const DisassemblerOptions* options_; 111 }; 112 113 public: 114 CustomDisassembler(std::ostream& os, const DisassemblerOptions* options) 115 : PrintDisassembler(&disassembler_stream_), 116 disassembler_stream_(os, this, options), 117 is_t32_(true) {} 118 119 void PrintCodeAddress(uint32_t prog_ctr) override { 120 os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << prog_ctr << ": "; 121 } 122 123 void SetIsT32(bool is_t32) { 124 is_t32_ = is_t32; 125 } 126 127 bool GetIsT32() const { 128 return is_t32_; 129 } 130 131 private: 132 CustomDisassemblerStream disassembler_stream_; 133 // Whether T32 stream is decoded. 134 bool is_t32_; 135 }; 136 137 void DisassemblerArm::CustomDisassembler::CustomDisassemblerStream::PrintLiteral(LocationType type, 138 int32_t offset) { 139 // Literal offsets are not required to be aligned, so we may need unaligned access. 140 using unaligned_int16_t __attribute__((__aligned__(1))) = const int16_t; 141 using unaligned_uint16_t __attribute__((__aligned__(1))) = const uint16_t; 142 using unaligned_int32_t __attribute__((__aligned__(1))) = const int32_t; 143 using unaligned_int64_t __attribute__((__aligned__(1))) = const int64_t; 144 using unaligned_float __attribute__((__aligned__(1))) = const float; 145 using unaligned_double __attribute__((__aligned__(1))) = const double; 146 147 // Zeros are used for the LocationType values this function does not care about. 148 const size_t literal_size[kVst4Location + 1] = { 149 0, 0, 0, 0, sizeof(uint8_t), sizeof(unaligned_uint16_t), sizeof(unaligned_int32_t), 150 sizeof(unaligned_int64_t), sizeof(int8_t), sizeof(unaligned_int16_t), 151 sizeof(unaligned_float), sizeof(unaligned_double)}; 152 const uintptr_t begin = reinterpret_cast<uintptr_t>(options_->base_address_); 153 const uintptr_t end = reinterpret_cast<uintptr_t>(options_->end_address_); 154 uintptr_t literal_addr = 155 RoundDown(disasm_->GetCodeAddress(), vixl::aarch32::kRegSizeInBytes) + offset; 156 literal_addr += disasm_->GetIsT32() ? vixl::aarch32::kT32PcDelta : vixl::aarch32::kA32PcDelta; 157 158 if (!options_->absolute_addresses_) { 159 literal_addr += begin; 160 } 161 162 os() << " ; "; 163 164 // Bail out if not within expected buffer range to avoid trying to fetch invalid literals 165 // (we can encounter them when interpreting raw data as instructions). 166 if (literal_addr < begin || literal_addr > end - literal_size[type]) { 167 os() << "(?)"; 168 } else { 169 switch (type) { 170 case kLoadByteLocation: 171 os() << *reinterpret_cast<const uint8_t*>(literal_addr); 172 break; 173 case kLoadHalfWordLocation: 174 os() << *reinterpret_cast<unaligned_uint16_t*>(literal_addr); 175 break; 176 case kLoadWordLocation: { 177 const int32_t value = *reinterpret_cast<unaligned_int32_t*>(literal_addr); 178 os() << "0x" << std::hex << std::setw(8) << std::setfill('0') << value; 179 break; 180 } 181 case kLoadDoubleWordLocation: { 182 const int64_t value = *reinterpret_cast<unaligned_int64_t*>(literal_addr); 183 os() << "0x" << std::hex << std::setw(16) << std::setfill('0') << value; 184 break; 185 } 186 case kLoadSignedByteLocation: 187 os() << *reinterpret_cast<const int8_t*>(literal_addr); 188 break; 189 case kLoadSignedHalfWordLocation: 190 os() << *reinterpret_cast<unaligned_int16_t*>(literal_addr); 191 break; 192 case kLoadSinglePrecisionLocation: 193 os() << *reinterpret_cast<unaligned_float*>(literal_addr); 194 break; 195 case kLoadDoublePrecisionLocation: 196 os() << *reinterpret_cast<unaligned_double*>(literal_addr); 197 break; 198 default: 199 UNIMPLEMENTED(FATAL) << "Unexpected literal type: " << type; 200 } 201 } 202 } 203 204 DisassemblerArm::DisassemblerArm(DisassemblerOptions* options) 205 : Disassembler(options), disasm_(std::make_unique<CustomDisassembler>(output_, options)) {} 206 207 size_t DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin) { 208 uintptr_t next; 209 // Remove the Thumb specifier bit; no effect if begin does not point to T32 code. 210 const uintptr_t instr_ptr = reinterpret_cast<uintptr_t>(begin) & ~1; 211 212 const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0; 213 disasm_->SetCodeAddress(GetPc(instr_ptr)); 214 disasm_->SetIsT32(is_t32); 215 216 if (is_t32) { 217 const uint16_t* const ip = reinterpret_cast<const uint16_t*>(instr_ptr); 218 const uint16_t* const end_address = reinterpret_cast<const uint16_t*>( 219 GetDisassemblerOptions()->end_address_); 220 next = reinterpret_cast<uintptr_t>(disasm_->DecodeT32At(ip, end_address)); 221 } else { 222 const uint32_t* const ip = reinterpret_cast<const uint32_t*>(instr_ptr); 223 next = reinterpret_cast<uintptr_t>(disasm_->DecodeA32At(ip)); 224 } 225 226 os << output_.str(); 227 output_.str(std::string()); 228 return next - instr_ptr; 229 } 230 231 void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { 232 DCHECK_LE(begin, end); 233 234 // Remove the Thumb specifier bit; no effect if begin does not point to T32 code. 235 const uintptr_t base = reinterpret_cast<uintptr_t>(begin) & ~1; 236 237 const bool is_t32 = (reinterpret_cast<uintptr_t>(begin) & 1) != 0; 238 disasm_->SetCodeAddress(GetPc(base)); 239 disasm_->SetIsT32(is_t32); 240 241 if (is_t32) { 242 // The Thumb specifier bits cancel each other. 243 disasm_->DisassembleT32Buffer(reinterpret_cast<const uint16_t*>(base), end - begin); 244 } else { 245 disasm_->DisassembleA32Buffer(reinterpret_cast<const uint32_t*>(base), end - begin); 246 } 247 248 os << output_.str(); 249 output_.str(std::string()); 250 } 251 252 } // namespace arm 253 } // namespace art 254