| //===-- DisassemblerLLVMC.cpp ---------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DisassemblerLLVMC.h" | |
| #include "llvm-c/Disassembler.h" | |
| #include "llvm/ADT/SmallString.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" | |
| #include "llvm/MC/MCDisassembler/MCExternalSymbolizer.h" | |
| #include "llvm/MC/MCDisassembler/MCRelocationInfo.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstPrinter.h" | |
| #include "llvm/MC/MCInstrAnalysis.h" | |
| #include "llvm/MC/MCInstrInfo.h" | |
| #include "llvm/MC/MCRegisterInfo.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/MCTargetOptions.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/ScopedPrinter.h" | |
| #include "llvm/Support/TargetSelect.h" | |
| #include "llvm/TargetParser/AArch64TargetParser.h" | |
| #include "lldb/Core/Address.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/SymbolContext.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/SectionLoadList.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/DataExtractor.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/RegularExpression.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include <algorithm> | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| LLDB_PLUGIN_DEFINE(DisassemblerLLVMC) | |
| class DisassemblerLLVMC::MCDisasmInstance { | |
| public: | |
| static std::unique_ptr<MCDisasmInstance> | |
| Create(const char *triple, const char *cpu, const char *features_str, | |
| unsigned flavor, DisassemblerLLVMC &owner); | |
| ~MCDisasmInstance() = default; | |
| bool GetMCInst(const uint8_t *opcode_data, size_t opcode_data_len, | |
| lldb::addr_t pc, llvm::MCInst &mc_inst, uint64_t &size) const; | |
| void PrintMCInst(llvm::MCInst &mc_inst, lldb::addr_t pc, | |
| std::string &inst_string, std::string &comments_string); | |
| void SetStyle(bool use_hex_immed, HexImmediateStyle hex_style); | |
| void SetUseColor(bool use_color); | |
| bool GetUseColor() const; | |
| bool CanBranch(llvm::MCInst &mc_inst) const; | |
| bool HasDelaySlot(llvm::MCInst &mc_inst) const; | |
| bool IsCall(llvm::MCInst &mc_inst) const; | |
| bool IsLoad(llvm::MCInst &mc_inst) const; | |
| bool IsBarrier(llvm::MCInst &mc_inst) const; | |
| bool IsAuthenticated(llvm::MCInst &mc_inst) const; | |
| private: | |
| MCDisasmInstance(std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up, | |
| std::unique_ptr<llvm::MCRegisterInfo> &®_info_up, | |
| std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up, | |
| llvm::MCTargetOptions mc_options, | |
| std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up, | |
| std::unique_ptr<llvm::MCContext> &&context_up, | |
| std::unique_ptr<llvm::MCDisassembler> &&disasm_up, | |
| std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up, | |
| std::unique_ptr<llvm::MCInstrAnalysis> &&instr_analysis_up); | |
| std::unique_ptr<llvm::MCInstrInfo> m_instr_info_up; | |
| std::unique_ptr<llvm::MCRegisterInfo> m_reg_info_up; | |
| std::unique_ptr<llvm::MCSubtargetInfo> m_subtarget_info_up; | |
| llvm::MCTargetOptions m_mc_options; | |
| std::unique_ptr<llvm::MCAsmInfo> m_asm_info_up; | |
| std::unique_ptr<llvm::MCContext> m_context_up; | |
| std::unique_ptr<llvm::MCDisassembler> m_disasm_up; | |
| std::unique_ptr<llvm::MCInstPrinter> m_instr_printer_up; | |
| std::unique_ptr<llvm::MCInstrAnalysis> m_instr_analysis_up; | |
| }; | |
| namespace x86 { | |
| /// These are the three values deciding instruction control flow kind. | |
| /// InstructionLengthDecode function decodes an instruction and get this struct. | |
| /// | |
| /// primary_opcode | |
| /// Primary opcode of the instruction. | |
| /// For one-byte opcode instruction, it's the first byte after prefix. | |
| /// For two- and three-byte opcodes, it's the second byte. | |
| /// | |
| /// opcode_len | |
| /// The length of opcode in bytes. Valid opcode lengths are 1, 2, or 3. | |
| /// | |
| /// modrm | |
| /// ModR/M byte of the instruction. | |
| /// Bits[7:6] indicate MOD. Bits[5:3] specify a register and R/M bits[2:0] | |
| /// may contain a register or specify an addressing mode, depending on MOD. | |
| struct InstructionOpcodeAndModrm { | |
| uint8_t primary_opcode; | |
| uint8_t opcode_len; | |
| uint8_t modrm; | |
| }; | |
| /// Determine the InstructionControlFlowKind based on opcode and modrm bytes. | |
| /// Refer to http://ref.x86asm.net/coder.html for the full list of opcode and | |
| /// instruction set. | |
| /// | |
| /// \param[in] opcode_and_modrm | |
| /// Contains primary_opcode byte, its length, and ModR/M byte. | |
| /// Refer to the struct InstructionOpcodeAndModrm for details. | |
| /// | |
| /// \return | |
| /// The control flow kind of the instruction or | |
| /// eInstructionControlFlowKindOther if the instruction doesn't affect | |
| /// the control flow of the program. | |
| lldb::InstructionControlFlowKind | |
| MapOpcodeIntoControlFlowKind(InstructionOpcodeAndModrm opcode_and_modrm) { | |
| uint8_t opcode = opcode_and_modrm.primary_opcode; | |
| uint8_t opcode_len = opcode_and_modrm.opcode_len; | |
| uint8_t modrm = opcode_and_modrm.modrm; | |
| if (opcode_len > 2) | |
| return lldb::eInstructionControlFlowKindOther; | |
| if (opcode >= 0x70 && opcode <= 0x7F) { | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindCondJump; | |
| else | |
| return lldb::eInstructionControlFlowKindOther; | |
| } | |
| if (opcode >= 0x80 && opcode <= 0x8F) { | |
| if (opcode_len == 2) | |
| return lldb::eInstructionControlFlowKindCondJump; | |
| else | |
| return lldb::eInstructionControlFlowKindOther; | |
| } | |
| switch (opcode) { | |
| case 0x9A: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindFarCall; | |
| break; | |
| case 0xFF: | |
| if (opcode_len == 1) { | |
| uint8_t modrm_reg = (modrm >> 3) & 7; | |
| if (modrm_reg == 2) | |
| return lldb::eInstructionControlFlowKindCall; | |
| else if (modrm_reg == 3) | |
| return lldb::eInstructionControlFlowKindFarCall; | |
| else if (modrm_reg == 4) | |
| return lldb::eInstructionControlFlowKindJump; | |
| else if (modrm_reg == 5) | |
| return lldb::eInstructionControlFlowKindFarJump; | |
| } | |
| break; | |
| case 0xE8: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindCall; | |
| break; | |
| case 0xCD: | |
| case 0xCC: | |
| case 0xCE: | |
| case 0xF1: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindFarCall; | |
| break; | |
| case 0xCF: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindFarReturn; | |
| break; | |
| case 0xE9: | |
| case 0xEB: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindJump; | |
| break; | |
| case 0xEA: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindFarJump; | |
| break; | |
| case 0xE3: | |
| case 0xE0: | |
| case 0xE1: | |
| case 0xE2: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindCondJump; | |
| break; | |
| case 0xC3: | |
| case 0xC2: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindReturn; | |
| break; | |
| case 0xCB: | |
| case 0xCA: | |
| if (opcode_len == 1) | |
| return lldb::eInstructionControlFlowKindFarReturn; | |
| break; | |
| case 0x05: | |
| case 0x34: | |
| if (opcode_len == 2) | |
| return lldb::eInstructionControlFlowKindFarCall; | |
| break; | |
| case 0x35: | |
| case 0x07: | |
| if (opcode_len == 2) | |
| return lldb::eInstructionControlFlowKindFarReturn; | |
| break; | |
| case 0x01: | |
| if (opcode_len == 2) { | |
| switch (modrm) { | |
| case 0xc1: | |
| return lldb::eInstructionControlFlowKindFarCall; | |
| case 0xc2: | |
| case 0xc3: | |
| return lldb::eInstructionControlFlowKindFarReturn; | |
| default: | |
| break; | |
| } | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return lldb::eInstructionControlFlowKindOther; | |
| } | |
| /// Decode an instruction into opcode, modrm and opcode_len. | |
| /// Refer to http://ref.x86asm.net/coder.html for the instruction bytes layout. | |
| /// Opcodes in x86 are generally the first byte of instruction, though two-byte | |
| /// instructions and prefixes exist. ModR/M is the byte following the opcode | |
| /// and adds additional information for how the instruction is executed. | |
| /// | |
| /// \param[in] inst_bytes | |
| /// Raw bytes of the instruction | |
| /// | |
| /// | |
| /// \param[in] bytes_len | |
| /// The length of the inst_bytes array. | |
| /// | |
| /// \param[in] is_exec_mode_64b | |
| /// If true, the execution mode is 64 bit. | |
| /// | |
| /// \return | |
| /// Returns decoded instruction as struct InstructionOpcodeAndModrm, holding | |
| /// primary_opcode, opcode_len and modrm byte. Refer to the struct definition | |
| /// for more details. | |
| /// Otherwise if the given instruction is invalid, returns std::nullopt. | |
| std::optional<InstructionOpcodeAndModrm> | |
| InstructionLengthDecode(const uint8_t *inst_bytes, int bytes_len, | |
| bool is_exec_mode_64b) { | |
| int op_idx = 0; | |
| bool prefix_done = false; | |
| InstructionOpcodeAndModrm ret = {0, 0, 0}; | |
| // In most cases, the primary_opcode is the first byte of the instruction | |
| // but some instructions have a prefix to be skipped for these calculations. | |
| // The following mapping is inspired from libipt's instruction decoding logic | |
| // in `src/pt_ild.c` | |
| while (!prefix_done) { | |
| if (op_idx >= bytes_len) | |
| return std::nullopt; | |
| ret.primary_opcode = inst_bytes[op_idx]; | |
| switch (ret.primary_opcode) { | |
| // prefix_ignore | |
| case 0x26: | |
| case 0x2e: | |
| case 0x36: | |
| case 0x3e: | |
| case 0x64: | |
| case 0x65: | |
| // prefix_osz, prefix_asz | |
| case 0x66: | |
| case 0x67: | |
| // prefix_lock, prefix_f2, prefix_f3 | |
| case 0xf0: | |
| case 0xf2: | |
| case 0xf3: | |
| op_idx++; | |
| break; | |
| // prefix_rex | |
| case 0x40: | |
| case 0x41: | |
| case 0x42: | |
| case 0x43: | |
| case 0x44: | |
| case 0x45: | |
| case 0x46: | |
| case 0x47: | |
| case 0x48: | |
| case 0x49: | |
| case 0x4a: | |
| case 0x4b: | |
| case 0x4c: | |
| case 0x4d: | |
| case 0x4e: | |
| case 0x4f: | |
| if (is_exec_mode_64b) | |
| op_idx++; | |
| else | |
| prefix_done = true; | |
| break; | |
| // prefix_vex_c4, c5 | |
| case 0xc5: | |
| if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { | |
| prefix_done = true; | |
| break; | |
| } | |
| ret.opcode_len = 2; | |
| ret.primary_opcode = inst_bytes[op_idx + 2]; | |
| ret.modrm = inst_bytes[op_idx + 3]; | |
| return ret; | |
| case 0xc4: | |
| if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { | |
| prefix_done = true; | |
| break; | |
| } | |
| ret.opcode_len = inst_bytes[op_idx + 1] & 0x1f; | |
| ret.primary_opcode = inst_bytes[op_idx + 3]; | |
| ret.modrm = inst_bytes[op_idx + 4]; | |
| return ret; | |
| // prefix_evex | |
| case 0x62: | |
| if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) { | |
| prefix_done = true; | |
| break; | |
| } | |
| ret.opcode_len = inst_bytes[op_idx + 1] & 0x03; | |
| ret.primary_opcode = inst_bytes[op_idx + 4]; | |
| ret.modrm = inst_bytes[op_idx + 5]; | |
| return ret; | |
| default: | |
| prefix_done = true; | |
| break; | |
| } | |
| } // prefix done | |
| ret.primary_opcode = inst_bytes[op_idx]; | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| ret.opcode_len = 1; | |
| // If the first opcode is 0F, it's two- or three- byte opcodes. | |
| if (ret.primary_opcode == 0x0F) { | |
| ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte | |
| if (ret.primary_opcode == 0x38) { | |
| ret.opcode_len = 3; | |
| ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| } else if (ret.primary_opcode == 0x3A) { | |
| ret.opcode_len = 3; | |
| ret.primary_opcode = inst_bytes[++op_idx]; | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| } else if ((ret.primary_opcode & 0xf8) == 0x38) { | |
| ret.opcode_len = 0; | |
| ret.primary_opcode = inst_bytes[++op_idx]; | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| } else if (ret.primary_opcode == 0x0F) { | |
| ret.opcode_len = 3; | |
| // opcode is 0x0F, no needs to update | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| } else { | |
| ret.opcode_len = 2; | |
| ret.modrm = inst_bytes[op_idx + 1]; | |
| } | |
| } | |
| return ret; | |
| } | |
| lldb::InstructionControlFlowKind GetControlFlowKind(bool is_exec_mode_64b, | |
| Opcode m_opcode) { | |
| std::optional<InstructionOpcodeAndModrm> ret; | |
| if (m_opcode.GetOpcodeBytes() == nullptr || m_opcode.GetByteSize() <= 0) { | |
| // x86_64 and i386 instructions are categorized as Opcode::Type::eTypeBytes | |
| return lldb::eInstructionControlFlowKindUnknown; | |
| } | |
| // Opcode bytes will be decoded into primary_opcode, modrm and opcode length. | |
| // These are the three values deciding instruction control flow kind. | |
| ret = InstructionLengthDecode((const uint8_t *)m_opcode.GetOpcodeBytes(), | |
| m_opcode.GetByteSize(), is_exec_mode_64b); | |
| if (!ret) | |
| return lldb::eInstructionControlFlowKindUnknown; | |
| else | |
| return MapOpcodeIntoControlFlowKind(*ret); | |
| } | |
| } // namespace x86 | |
| class InstructionLLVMC : public lldb_private::Instruction { | |
| public: | |
| InstructionLLVMC(DisassemblerLLVMC &disasm, | |
| const lldb_private::Address &address, | |
| AddressClass addr_class) | |
| : Instruction(address, addr_class), | |
| m_disasm_wp(std::static_pointer_cast<DisassemblerLLVMC>( | |
| disasm.shared_from_this())) {} | |
| ~InstructionLLVMC() override = default; | |
| bool DoesBranch() override { | |
| VisitInstruction(); | |
| return m_does_branch; | |
| } | |
| bool HasDelaySlot() override { | |
| VisitInstruction(); | |
| return m_has_delay_slot; | |
| } | |
| bool IsLoad() override { | |
| VisitInstruction(); | |
| return m_is_load; | |
| } | |
| bool IsBarrier() override { | |
| VisitInstruction(); | |
| return m_is_barrier; | |
| } | |
| bool IsAuthenticated() override { | |
| VisitInstruction(); | |
| return m_is_authenticated; | |
| } | |
| DisassemblerLLVMC::MCDisasmInstance *GetDisasmToUse(bool &is_alternate_isa) { | |
| DisassemblerScope disasm(*this); | |
| return GetDisasmToUse(is_alternate_isa, disasm); | |
| } | |
| size_t Decode(const lldb_private::Disassembler &disassembler, | |
| const lldb_private::DataExtractor &data, | |
| lldb::offset_t data_offset) override { | |
| // All we have to do is read the opcode which can be easy for some | |
| // architectures | |
| DisassemblerScope disasm(*this); | |
| if (disasm) { | |
| const ArchSpec &arch = disasm->GetArchitecture(); | |
| const lldb::ByteOrder byte_order = data.GetByteOrder(); | |
| const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize(); | |
| const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize(); | |
| if (min_op_byte_size == max_op_byte_size) { | |
| // Fixed size instructions, just read that amount of data. | |
| if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size)) | |
| return 0; | |
| switch (min_op_byte_size) { | |
| case 1: | |
| m_opcode.SetOpcode8(data.GetU8(&data_offset), byte_order); | |
| break; | |
| case 2: | |
| m_opcode.SetOpcode16(data.GetU16(&data_offset), byte_order); | |
| break; | |
| case 4: | |
| m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order); | |
| break; | |
| case 8: | |
| m_opcode.SetOpcode64(data.GetU64(&data_offset), byte_order); | |
| break; | |
| default: | |
| m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), | |
| min_op_byte_size); | |
| break; | |
| } | |
| } else { | |
| bool is_alternate_isa = false; | |
| DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr = | |
| GetDisasmToUse(is_alternate_isa, disasm); | |
| const llvm::Triple::ArchType machine = arch.GetMachine(); | |
| if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) { | |
| if (machine == llvm::Triple::thumb || is_alternate_isa) { | |
| uint32_t thumb_opcode = data.GetU16(&data_offset); | |
| if ((thumb_opcode & 0xe000) != 0xe000 || | |
| ((thumb_opcode & 0x1800u) == 0)) { | |
| m_opcode.SetOpcode16(thumb_opcode, byte_order); | |
| m_is_valid = true; | |
| } else { | |
| thumb_opcode <<= 16; | |
| thumb_opcode |= data.GetU16(&data_offset); | |
| m_opcode.SetOpcode16_2(thumb_opcode, byte_order); | |
| m_is_valid = true; | |
| } | |
| } else { | |
| m_opcode.SetOpcode32(data.GetU32(&data_offset), byte_order); | |
| m_is_valid = true; | |
| } | |
| } else { | |
| // The opcode isn't evenly sized, so we need to actually use the llvm | |
| // disassembler to parse it and get the size. | |
| uint8_t *opcode_data = | |
| const_cast<uint8_t *>(data.PeekData(data_offset, 1)); | |
| const size_t opcode_data_len = data.BytesLeft(data_offset); | |
| const addr_t pc = m_address.GetFileAddress(); | |
| llvm::MCInst inst; | |
| uint64_t inst_size = 0; | |
| m_is_valid = mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, | |
| pc, inst, inst_size); | |
| m_opcode.Clear(); | |
| inst_size = std::min<uint64_t>(inst_size, Opcode::kMaxByteSize); | |
| if (inst_size != 0) { | |
| if (arch.GetTriple().isRISCV()) | |
| m_opcode.SetOpcode16_32TupleBytes(opcode_data, inst_size, | |
| byte_order); | |
| else | |
| m_opcode.SetOpcodeBytes(opcode_data, inst_size); | |
| } | |
| } | |
| } | |
| return m_opcode.GetByteSize(); | |
| } | |
| return 0; | |
| } | |
| void AppendComment(std::string &description) { | |
| if (m_comment.empty()) | |
| m_comment.swap(description); | |
| else { | |
| m_comment.append(", "); | |
| m_comment.append(description); | |
| } | |
| } | |
| lldb::InstructionControlFlowKind | |
| GetControlFlowKind(const lldb_private::ExecutionContext *exe_ctx) override { | |
| DisassemblerScope disasm(*this, exe_ctx); | |
| if (disasm) { | |
| if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86) | |
| return x86::GetControlFlowKind(/*is_64b=*/false, m_opcode); | |
| else if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86_64) | |
| return x86::GetControlFlowKind(/*is_64b=*/true, m_opcode); | |
| } | |
| return eInstructionControlFlowKindUnknown; | |
| } | |
| void CalculateMnemonicOperandsAndComment( | |
| const lldb_private::ExecutionContext *exe_ctx) override { | |
| DataExtractor data; | |
| const AddressClass address_class = GetAddressClass(); | |
| if (m_opcode.GetData(data)) { | |
| std::string out_string; | |
| std::string markup_out_string; | |
| std::string comment_string; | |
| std::string markup_comment_string; | |
| DisassemblerScope disasm(*this, exe_ctx); | |
| if (disasm) { | |
| DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr; | |
| if (address_class == AddressClass::eCodeAlternateISA) | |
| mc_disasm_ptr = disasm->m_alternate_disasm_up.get(); | |
| else | |
| mc_disasm_ptr = disasm->m_disasm_up.get(); | |
| lldb::addr_t pc = m_address.GetFileAddress(); | |
| m_using_file_addr = true; | |
| bool use_hex_immediates = true; | |
| Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC; | |
| if (exe_ctx) { | |
| Target *target = exe_ctx->GetTargetPtr(); | |
| if (target) { | |
| use_hex_immediates = target->GetUseHexImmediates(); | |
| hex_style = target->GetHexImmediateStyle(); | |
| const lldb::addr_t load_addr = m_address.GetLoadAddress(target); | |
| if (load_addr != LLDB_INVALID_ADDRESS) { | |
| pc = load_addr; | |
| m_using_file_addr = false; | |
| } | |
| } | |
| } | |
| const uint8_t *opcode_data = data.GetDataStart(); | |
| const size_t opcode_data_len = data.GetByteSize(); | |
| llvm::MCInst inst; | |
| uint64_t inst_size = 0; | |
| bool valid = mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, | |
| inst, inst_size); | |
| if (valid && inst_size > 0) { | |
| mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style); | |
| const bool saved_use_color = mc_disasm_ptr->GetUseColor(); | |
| mc_disasm_ptr->SetUseColor(false); | |
| mc_disasm_ptr->PrintMCInst(inst, pc, out_string, comment_string); | |
| mc_disasm_ptr->SetUseColor(true); | |
| mc_disasm_ptr->PrintMCInst(inst, pc, markup_out_string, | |
| markup_comment_string); | |
| mc_disasm_ptr->SetUseColor(saved_use_color); | |
| if (!comment_string.empty()) { | |
| AppendComment(comment_string); | |
| } | |
| } | |
| if (inst_size == 0) { | |
| m_comment.assign("unknown opcode"); | |
| inst_size = m_opcode.GetByteSize(); | |
| StreamString mnemonic_strm; | |
| lldb::offset_t offset = 0; | |
| lldb::ByteOrder byte_order = data.GetByteOrder(); | |
| switch (inst_size) { | |
| case 1: { | |
| const uint8_t uval8 = data.GetU8(&offset); | |
| m_opcode.SetOpcode8(uval8, byte_order); | |
| m_opcode_name.assign(".byte"); | |
| mnemonic_strm.Printf("0x%2.2x", uval8); | |
| } break; | |
| case 2: { | |
| const uint16_t uval16 = data.GetU16(&offset); | |
| m_opcode.SetOpcode16(uval16, byte_order); | |
| m_opcode_name.assign(".short"); | |
| mnemonic_strm.Printf("0x%4.4x", uval16); | |
| } break; | |
| case 4: { | |
| const uint32_t uval32 = data.GetU32(&offset); | |
| m_opcode.SetOpcode32(uval32, byte_order); | |
| m_opcode_name.assign(".long"); | |
| mnemonic_strm.Printf("0x%8.8x", uval32); | |
| } break; | |
| case 8: { | |
| const uint64_t uval64 = data.GetU64(&offset); | |
| m_opcode.SetOpcode64(uval64, byte_order); | |
| m_opcode_name.assign(".quad"); | |
| mnemonic_strm.Printf("0x%16.16" PRIx64, uval64); | |
| } break; | |
| default: | |
| if (inst_size == 0) | |
| return; | |
| else { | |
| const uint8_t *bytes = data.PeekData(offset, inst_size); | |
| if (bytes == nullptr) | |
| return; | |
| m_opcode_name.assign(".byte"); | |
| m_opcode.SetOpcodeBytes(bytes, inst_size); | |
| mnemonic_strm.Printf("0x%2.2x", bytes[0]); | |
| for (uint32_t i = 1; i < inst_size; ++i) | |
| mnemonic_strm.Printf(" 0x%2.2x", bytes[i]); | |
| } | |
| break; | |
| } | |
| m_mnemonics = std::string(mnemonic_strm.GetString()); | |
| return; | |
| } | |
| static RegularExpression s_regex( | |
| llvm::StringRef("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?")); | |
| llvm::SmallVector<llvm::StringRef, 4> matches; | |
| if (s_regex.Execute(out_string, &matches)) { | |
| m_opcode_name = matches[1].str(); | |
| m_mnemonics = matches[2].str(); | |
| } | |
| matches.clear(); | |
| if (s_regex.Execute(markup_out_string, &matches)) { | |
| m_markup_opcode_name = matches[1].str(); | |
| m_markup_mnemonics = matches[2].str(); | |
| } | |
| } | |
| } | |
| } | |
| bool IsValid() const { return m_is_valid; } | |
| bool UsingFileAddress() const { return m_using_file_addr; } | |
| size_t GetByteSize() const { return m_opcode.GetByteSize(); } | |
| /// Grants exclusive access to the disassembler and initializes it with the | |
| /// given InstructionLLVMC and an optional ExecutionContext. | |
| class DisassemblerScope { | |
| std::shared_ptr<DisassemblerLLVMC> m_disasm; | |
| public: | |
| explicit DisassemblerScope( | |
| InstructionLLVMC &i, | |
| const lldb_private::ExecutionContext *exe_ctx = nullptr) | |
| : m_disasm(i.m_disasm_wp.lock()) { | |
| m_disasm->m_mutex.lock(); | |
| m_disasm->m_inst = &i; | |
| m_disasm->m_exe_ctx = exe_ctx; | |
| } | |
| ~DisassemblerScope() { m_disasm->m_mutex.unlock(); } | |
| /// Evaluates to true if this scope contains a valid disassembler. | |
| operator bool() const { return static_cast<bool>(m_disasm); } | |
| std::shared_ptr<DisassemblerLLVMC> operator->() { return m_disasm; } | |
| }; | |
| static llvm::StringRef::const_iterator | |
| ConsumeWhitespace(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| while (osi != ose) { | |
| switch (*osi) { | |
| default: | |
| return osi; | |
| case ' ': | |
| case '\t': | |
| break; | |
| } | |
| ++osi; | |
| } | |
| return osi; | |
| } | |
| static std::pair<bool, llvm::StringRef::const_iterator> | |
| ConsumeChar(llvm::StringRef::const_iterator osi, const char c, | |
| llvm::StringRef::const_iterator ose) { | |
| bool found = false; | |
| osi = ConsumeWhitespace(osi, ose); | |
| if (osi != ose && *osi == c) { | |
| found = true; | |
| ++osi; | |
| } | |
| return std::make_pair(found, osi); | |
| } | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseRegisterName(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| Operand ret; | |
| ret.m_type = Operand::Type::Register; | |
| std::string str; | |
| osi = ConsumeWhitespace(osi, ose); | |
| while (osi != ose) { | |
| if (*osi >= '0' && *osi <= '9') { | |
| if (str.empty()) { | |
| return std::make_pair(Operand(), osi); | |
| } else { | |
| str.push_back(*osi); | |
| } | |
| } else if (*osi >= 'a' && *osi <= 'z') { | |
| str.push_back(*osi); | |
| } else { | |
| switch (*osi) { | |
| default: | |
| if (str.empty()) { | |
| return std::make_pair(Operand(), osi); | |
| } else { | |
| ret.m_register = str; | |
| return std::make_pair(ret, osi); | |
| } | |
| case '%': | |
| if (!str.empty()) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| break; | |
| } | |
| } | |
| ++osi; | |
| } | |
| ret.m_register = str; | |
| return std::make_pair(ret, osi); | |
| } | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseImmediate(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| Operand ret; | |
| ret.m_type = Operand::Type::Immediate; | |
| std::string str; | |
| bool is_hex = false; | |
| osi = ConsumeWhitespace(osi, ose); | |
| while (osi != ose) { | |
| if (*osi >= '0' && *osi <= '9') { | |
| str.push_back(*osi); | |
| } else if (*osi >= 'a' && *osi <= 'f') { | |
| if (is_hex) { | |
| str.push_back(*osi); | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| } else { | |
| switch (*osi) { | |
| default: | |
| if (str.empty()) { | |
| return std::make_pair(Operand(), osi); | |
| } else { | |
| ret.m_immediate = strtoull(str.c_str(), nullptr, 0); | |
| return std::make_pair(ret, osi); | |
| } | |
| case 'x': | |
| if (str == "0") { | |
| is_hex = true; | |
| str.push_back(*osi); | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| break; | |
| case '#': | |
| case '$': | |
| if (!str.empty()) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| break; | |
| case '-': | |
| if (str.empty()) { | |
| ret.m_negative = true; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| } | |
| } | |
| ++osi; | |
| } | |
| ret.m_immediate = strtoull(str.c_str(), nullptr, 0); | |
| return std::make_pair(ret, osi); | |
| } | |
| // -0x5(%rax,%rax,2) | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseIntelIndexedAccess(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = | |
| ParseImmediate(osi, ose); | |
| if (offset_and_iterator.first.IsValid()) { | |
| osi = offset_and_iterator.second; | |
| } | |
| bool found = false; | |
| std::tie(found, osi) = ConsumeChar(osi, '(', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = | |
| ParseRegisterName(osi, ose); | |
| if (base_and_iterator.first.IsValid()) { | |
| osi = base_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ',', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> index_and_iterator = | |
| ParseRegisterName(osi, ose); | |
| if (index_and_iterator.first.IsValid()) { | |
| osi = index_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ',', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> | |
| multiplier_and_iterator = ParseImmediate(osi, ose); | |
| if (index_and_iterator.first.IsValid()) { | |
| osi = index_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ')', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| Operand product; | |
| product.m_type = Operand::Type::Product; | |
| product.m_children.push_back(index_and_iterator.first); | |
| product.m_children.push_back(multiplier_and_iterator.first); | |
| Operand index; | |
| index.m_type = Operand::Type::Sum; | |
| index.m_children.push_back(base_and_iterator.first); | |
| index.m_children.push_back(product); | |
| if (offset_and_iterator.first.IsValid()) { | |
| Operand offset; | |
| offset.m_type = Operand::Type::Sum; | |
| offset.m_children.push_back(offset_and_iterator.first); | |
| offset.m_children.push_back(index); | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(offset); | |
| return std::make_pair(deref, osi); | |
| } else { | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(index); | |
| return std::make_pair(deref, osi); | |
| } | |
| } | |
| // -0x10(%rbp) | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseIntelDerefAccess(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = | |
| ParseImmediate(osi, ose); | |
| if (offset_and_iterator.first.IsValid()) { | |
| osi = offset_and_iterator.second; | |
| } | |
| bool found = false; | |
| std::tie(found, osi) = ConsumeChar(osi, '(', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = | |
| ParseRegisterName(osi, ose); | |
| if (base_and_iterator.first.IsValid()) { | |
| osi = base_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ')', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| if (offset_and_iterator.first.IsValid()) { | |
| Operand offset; | |
| offset.m_type = Operand::Type::Sum; | |
| offset.m_children.push_back(offset_and_iterator.first); | |
| offset.m_children.push_back(base_and_iterator.first); | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(offset); | |
| return std::make_pair(deref, osi); | |
| } else { | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(base_and_iterator.first); | |
| return std::make_pair(deref, osi); | |
| } | |
| } | |
| // [sp, #8]! | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseARMOffsetAccess(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| bool found = false; | |
| std::tie(found, osi) = ConsumeChar(osi, '[', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = | |
| ParseRegisterName(osi, ose); | |
| if (base_and_iterator.first.IsValid()) { | |
| osi = base_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ',', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> offset_and_iterator = | |
| ParseImmediate(osi, ose); | |
| if (offset_and_iterator.first.IsValid()) { | |
| osi = offset_and_iterator.second; | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ']', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| Operand offset; | |
| offset.m_type = Operand::Type::Sum; | |
| offset.m_children.push_back(offset_and_iterator.first); | |
| offset.m_children.push_back(base_and_iterator.first); | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(offset); | |
| return std::make_pair(deref, osi); | |
| } | |
| // [sp] | |
| static std::pair<Operand, llvm::StringRef::const_iterator> | |
| ParseARMDerefAccess(llvm::StringRef::const_iterator osi, | |
| llvm::StringRef::const_iterator ose) { | |
| bool found = false; | |
| std::tie(found, osi) = ConsumeChar(osi, '[', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::pair<Operand, llvm::StringRef::const_iterator> base_and_iterator = | |
| ParseRegisterName(osi, ose); | |
| if (base_and_iterator.first.IsValid()) { | |
| osi = base_and_iterator.second; | |
| } else { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| std::tie(found, osi) = ConsumeChar(osi, ']', ose); | |
| if (!found) { | |
| return std::make_pair(Operand(), osi); | |
| } | |
| Operand deref; | |
| deref.m_type = Operand::Type::Dereference; | |
| deref.m_children.push_back(base_and_iterator.first); | |
| return std::make_pair(deref, osi); | |
| } | |
| static void DumpOperand(const Operand &op, Stream &s) { | |
| switch (op.m_type) { | |
| case Operand::Type::Dereference: | |
| s.PutCString("*"); | |
| DumpOperand(op.m_children[0], s); | |
| break; | |
| case Operand::Type::Immediate: | |
| if (op.m_negative) { | |
| s.PutCString("-"); | |
| } | |
| s.PutCString(llvm::to_string(op.m_immediate)); | |
| break; | |
| case Operand::Type::Invalid: | |
| s.PutCString("Invalid"); | |
| break; | |
| case Operand::Type::Product: | |
| s.PutCString("("); | |
| DumpOperand(op.m_children[0], s); | |
| s.PutCString("*"); | |
| DumpOperand(op.m_children[1], s); | |
| s.PutCString(")"); | |
| break; | |
| case Operand::Type::Register: | |
| s.PutCString(op.m_register); | |
| break; | |
| case Operand::Type::Sum: | |
| s.PutCString("("); | |
| DumpOperand(op.m_children[0], s); | |
| s.PutCString("+"); | |
| DumpOperand(op.m_children[1], s); | |
| s.PutCString(")"); | |
| break; | |
| } | |
| } | |
| bool ParseOperands( | |
| llvm::SmallVectorImpl<Instruction::Operand> &operands) override { | |
| const char *operands_string = GetOperands(nullptr); | |
| if (!operands_string) { | |
| return false; | |
| } | |
| llvm::StringRef operands_ref(operands_string); | |
| llvm::StringRef::const_iterator osi = operands_ref.begin(); | |
| llvm::StringRef::const_iterator ose = operands_ref.end(); | |
| while (osi != ose) { | |
| Operand operand; | |
| llvm::StringRef::const_iterator iter; | |
| if ((std::tie(operand, iter) = ParseIntelIndexedAccess(osi, ose), | |
| operand.IsValid()) || | |
| (std::tie(operand, iter) = ParseIntelDerefAccess(osi, ose), | |
| operand.IsValid()) || | |
| (std::tie(operand, iter) = ParseARMOffsetAccess(osi, ose), | |
| operand.IsValid()) || | |
| (std::tie(operand, iter) = ParseARMDerefAccess(osi, ose), | |
| operand.IsValid()) || | |
| (std::tie(operand, iter) = ParseRegisterName(osi, ose), | |
| operand.IsValid()) || | |
| (std::tie(operand, iter) = ParseImmediate(osi, ose), | |
| operand.IsValid())) { | |
| osi = iter; | |
| operands.push_back(operand); | |
| } else { | |
| return false; | |
| } | |
| std::pair<bool, llvm::StringRef::const_iterator> found_and_iter = | |
| ConsumeChar(osi, ',', ose); | |
| if (found_and_iter.first) { | |
| osi = found_and_iter.second; | |
| } | |
| osi = ConsumeWhitespace(osi, ose); | |
| } | |
| DisassemblerSP disasm_sp = m_disasm_wp.lock(); | |
| if (disasm_sp && operands.size() > 1) { | |
| // TODO tie this into the MC Disassembler's notion of clobbers. | |
| switch (disasm_sp->GetArchitecture().GetMachine()) { | |
| default: | |
| break; | |
| case llvm::Triple::x86: | |
| case llvm::Triple::x86_64: | |
| operands[operands.size() - 1].m_clobbered = true; | |
| break; | |
| case llvm::Triple::arm: | |
| operands[0].m_clobbered = true; | |
| break; | |
| } | |
| } | |
| if (Log *log = GetLog(LLDBLog::Process | LLDBLog::Disassembler)) { | |
| StreamString ss; | |
| ss.Printf("[%s] expands to %zu operands:\n", operands_string, | |
| operands.size()); | |
| for (const Operand &operand : operands) { | |
| ss.PutCString(" "); | |
| DumpOperand(operand, ss); | |
| ss.PutCString("\n"); | |
| } | |
| log->PutString(ss.GetString()); | |
| } | |
| return true; | |
| } | |
| bool IsCall() override { | |
| VisitInstruction(); | |
| return m_is_call; | |
| } | |
| protected: | |
| std::weak_ptr<DisassemblerLLVMC> m_disasm_wp; | |
| bool m_is_valid = false; | |
| bool m_using_file_addr = false; | |
| bool m_has_visited_instruction = false; | |
| // Be conservative. If we didn't understand the instruction, say it: | |
| // - Might branch | |
| // - Does not have a delay slot | |
| // - Is not a call | |
| // - Is not a load | |
| // - Is not an authenticated instruction | |
| bool m_does_branch = true; | |
| bool m_has_delay_slot = false; | |
| bool m_is_call = false; | |
| bool m_is_load = false; | |
| bool m_is_authenticated = false; | |
| bool m_is_barrier = false; | |
| void VisitInstruction() { | |
| if (m_has_visited_instruction) | |
| return; | |
| DisassemblerScope disasm(*this); | |
| if (!disasm) | |
| return; | |
| DataExtractor data; | |
| if (!m_opcode.GetData(data)) | |
| return; | |
| bool is_alternate_isa; | |
| lldb::addr_t pc = m_address.GetFileAddress(); | |
| DisassemblerLLVMC::MCDisasmInstance *mc_disasm_ptr = | |
| GetDisasmToUse(is_alternate_isa, disasm); | |
| const uint8_t *opcode_data = data.GetDataStart(); | |
| const size_t opcode_data_len = data.GetByteSize(); | |
| llvm::MCInst inst; | |
| uint64_t inst_size = 0; | |
| const bool valid = mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, | |
| pc, inst, inst_size); | |
| if (!valid) | |
| return; | |
| m_has_visited_instruction = true; | |
| m_does_branch = mc_disasm_ptr->CanBranch(inst); | |
| m_has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst); | |
| m_is_call = mc_disasm_ptr->IsCall(inst); | |
| m_is_load = mc_disasm_ptr->IsLoad(inst); | |
| m_is_authenticated = mc_disasm_ptr->IsAuthenticated(inst); | |
| m_is_barrier = mc_disasm_ptr->IsBarrier(inst); | |
| } | |
| private: | |
| DisassemblerLLVMC::MCDisasmInstance * | |
| GetDisasmToUse(bool &is_alternate_isa, DisassemblerScope &disasm) { | |
| is_alternate_isa = false; | |
| if (disasm) { | |
| if (disasm->m_alternate_disasm_up) { | |
| const AddressClass address_class = GetAddressClass(); | |
| if (address_class == AddressClass::eCodeAlternateISA) { | |
| is_alternate_isa = true; | |
| return disasm->m_alternate_disasm_up.get(); | |
| } | |
| } | |
| return disasm->m_disasm_up.get(); | |
| } | |
| return nullptr; | |
| } | |
| }; | |
| std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance> | |
| DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple_name, | |
| const char *cpu, | |
| const char *features_str, | |
| unsigned flavor, | |
| DisassemblerLLVMC &owner) { | |
| using Instance = std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>; | |
| llvm::Triple triple(triple_name); | |
| std::string Status; | |
| const llvm::Target *curr_target = | |
| llvm::TargetRegistry::lookupTarget(triple, Status); | |
| if (!curr_target) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCInstrInfo> instr_info_up( | |
| curr_target->createMCInstrInfo()); | |
| if (!instr_info_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCRegisterInfo> reg_info_up( | |
| curr_target->createMCRegInfo(triple)); | |
| if (!reg_info_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCSubtargetInfo> subtarget_info_up( | |
| curr_target->createMCSubtargetInfo(triple, cpu, features_str)); | |
| if (!subtarget_info_up) | |
| return Instance(); | |
| llvm::MCTargetOptions MCOptions; | |
| std::unique_ptr<llvm::MCAsmInfo> asm_info_up( | |
| curr_target->createMCAsmInfo(*reg_info_up, triple, MCOptions)); | |
| if (!asm_info_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCContext> context_up(new llvm::MCContext( | |
| llvm::Triple(triple), *asm_info_up, *reg_info_up, *subtarget_info_up)); | |
| if (!context_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCDisassembler> disasm_up( | |
| curr_target->createMCDisassembler(*subtarget_info_up, *context_up)); | |
| if (!disasm_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCRelocationInfo> rel_info_up( | |
| curr_target->createMCRelocationInfo(triple, *context_up)); | |
| if (!rel_info_up) | |
| return Instance(); | |
| std::unique_ptr<llvm::MCSymbolizer> symbolizer_up( | |
| curr_target->createMCSymbolizer( | |
| triple, nullptr, DisassemblerLLVMC::SymbolLookupCallback, &owner, | |
| context_up.get(), std::move(rel_info_up))); | |
| disasm_up->setSymbolizer(std::move(symbolizer_up)); | |
| unsigned asm_printer_variant = | |
| flavor == ~0U ? asm_info_up->getAssemblerDialect() : flavor; | |
| std::unique_ptr<llvm::MCInstPrinter> instr_printer_up( | |
| curr_target->createMCInstPrinter(llvm::Triple{triple}, | |
| asm_printer_variant, *asm_info_up, | |
| *instr_info_up, *reg_info_up)); | |
| if (!instr_printer_up) | |
| return Instance(); | |
| instr_printer_up->setPrintBranchImmAsAddress(true); | |
| // Not all targets may have registered createMCInstrAnalysis(). | |
| std::unique_ptr<llvm::MCInstrAnalysis> instr_analysis_up( | |
| curr_target->createMCInstrAnalysis(instr_info_up.get())); | |
| return Instance(new MCDisasmInstance( | |
| std::move(instr_info_up), std::move(reg_info_up), | |
| std::move(subtarget_info_up), MCOptions, std::move(asm_info_up), | |
| std::move(context_up), std::move(disasm_up), std::move(instr_printer_up), | |
| std::move(instr_analysis_up))); | |
| } | |
| DisassemblerLLVMC::MCDisasmInstance::MCDisasmInstance( | |
| std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up, | |
| std::unique_ptr<llvm::MCRegisterInfo> &®_info_up, | |
| std::unique_ptr<llvm::MCSubtargetInfo> &&subtarget_info_up, | |
| llvm::MCTargetOptions mc_options, | |
| std::unique_ptr<llvm::MCAsmInfo> &&asm_info_up, | |
| std::unique_ptr<llvm::MCContext> &&context_up, | |
| std::unique_ptr<llvm::MCDisassembler> &&disasm_up, | |
| std::unique_ptr<llvm::MCInstPrinter> &&instr_printer_up, | |
| std::unique_ptr<llvm::MCInstrAnalysis> &&instr_analysis_up) | |
| : m_instr_info_up(std::move(instr_info_up)), | |
| m_reg_info_up(std::move(reg_info_up)), | |
| m_subtarget_info_up(std::move(subtarget_info_up)), | |
| m_mc_options(mc_options), m_asm_info_up(std::move(asm_info_up)), | |
| m_context_up(std::move(context_up)), m_disasm_up(std::move(disasm_up)), | |
| m_instr_printer_up(std::move(instr_printer_up)), | |
| m_instr_analysis_up(std::move(instr_analysis_up)) { | |
| assert(m_instr_info_up && m_reg_info_up && m_subtarget_info_up && | |
| m_asm_info_up && m_context_up && m_disasm_up && m_instr_printer_up); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::GetMCInst(const uint8_t *opcode_data, | |
| size_t opcode_data_len, | |
| lldb::addr_t pc, | |
| llvm::MCInst &mc_inst, | |
| uint64_t &size) const { | |
| llvm::ArrayRef<uint8_t> data(opcode_data, opcode_data_len); | |
| llvm::MCDisassembler::DecodeStatus status; | |
| status = m_disasm_up->getInstruction(mc_inst, size, data, pc, llvm::nulls()); | |
| if (status == llvm::MCDisassembler::Success) | |
| return true; | |
| else | |
| return false; | |
| } | |
| void DisassemblerLLVMC::MCDisasmInstance::PrintMCInst( | |
| llvm::MCInst &mc_inst, lldb::addr_t pc, std::string &inst_string, | |
| std::string &comments_string) { | |
| llvm::raw_string_ostream inst_stream(inst_string); | |
| llvm::raw_string_ostream comments_stream(comments_string); | |
| inst_stream.enable_colors(m_instr_printer_up->getUseColor()); | |
| m_instr_printer_up->setCommentStream(comments_stream); | |
| m_instr_printer_up->printInst(&mc_inst, pc, llvm::StringRef(), | |
| *m_subtarget_info_up, inst_stream); | |
| m_instr_printer_up->setCommentStream(llvm::nulls()); | |
| static std::string g_newlines("\r\n"); | |
| for (size_t newline_pos = 0; | |
| (newline_pos = comments_string.find_first_of(g_newlines, newline_pos)) != | |
| comments_string.npos; | |
| /**/) { | |
| comments_string.replace(comments_string.begin() + newline_pos, | |
| comments_string.begin() + newline_pos + 1, 1, ' '); | |
| } | |
| } | |
| void DisassemblerLLVMC::MCDisasmInstance::SetStyle( | |
| bool use_hex_immed, HexImmediateStyle hex_style) { | |
| m_instr_printer_up->setPrintImmHex(use_hex_immed); | |
| switch (hex_style) { | |
| case eHexStyleC: | |
| m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::C); | |
| break; | |
| case eHexStyleAsm: | |
| m_instr_printer_up->setPrintHexStyle(llvm::HexStyle::Asm); | |
| break; | |
| } | |
| } | |
| void DisassemblerLLVMC::MCDisasmInstance::SetUseColor(bool use_color) { | |
| m_instr_printer_up->setUseColor(use_color); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::GetUseColor() const { | |
| return m_instr_printer_up->getUseColor(); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::CanBranch( | |
| llvm::MCInst &mc_inst) const { | |
| if (m_instr_analysis_up) | |
| return m_instr_analysis_up->mayAffectControlFlow(mc_inst, *m_reg_info_up); | |
| return m_instr_info_up->get(mc_inst.getOpcode()) | |
| .mayAffectControlFlow(mc_inst, *m_reg_info_up); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::HasDelaySlot( | |
| llvm::MCInst &mc_inst) const { | |
| return m_instr_info_up->get(mc_inst.getOpcode()).hasDelaySlot(); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::IsCall(llvm::MCInst &mc_inst) const { | |
| if (m_instr_analysis_up) | |
| return m_instr_analysis_up->isCall(mc_inst); | |
| return m_instr_info_up->get(mc_inst.getOpcode()).isCall(); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::IsLoad(llvm::MCInst &mc_inst) const { | |
| return m_instr_info_up->get(mc_inst.getOpcode()).mayLoad(); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::IsBarrier( | |
| llvm::MCInst &mc_inst) const { | |
| return m_instr_info_up->get(mc_inst.getOpcode()).isBarrier(); | |
| } | |
| bool DisassemblerLLVMC::MCDisasmInstance::IsAuthenticated( | |
| llvm::MCInst &mc_inst) const { | |
| const auto &InstrDesc = m_instr_info_up->get(mc_inst.getOpcode()); | |
| // Treat software auth traps (brk 0xc470 + aut key, where 0x70 == 'p', 0xc4 | |
| // == 'a' + 'c') as authenticated instructions for reporting purposes, in | |
| // addition to the standard authenticated instructions specified in ARMv8.3. | |
| bool IsBrkC47x = false; | |
| if (InstrDesc.isTrap() && mc_inst.getNumOperands() == 1) { | |
| const llvm::MCOperand &Op0 = mc_inst.getOperand(0); | |
| if (Op0.isImm() && Op0.getImm() >= 0xc470 && Op0.getImm() <= 0xc474) | |
| IsBrkC47x = true; | |
| } | |
| return InstrDesc.isAuthenticated() || IsBrkC47x; | |
| } | |
| void DisassemblerLLVMC::UpdateSubtargetFeatures( | |
| llvm::StringRef subtarget_features, std::string &user_feature_overrides) { | |
| llvm::SmallVector<std::string, 0> valid_user_flags; | |
| llvm::StringSet<> user_disabled_features; | |
| for (llvm::StringRef flag : llvm::split(user_feature_overrides, ",")) { | |
| bool is_valid = true; | |
| flag = flag.trim(); | |
| if (flag.empty()) | |
| continue; | |
| std::string warning_reason; | |
| llvm::raw_string_ostream ostream(warning_reason); | |
| // 1. Must be at least 2 chars (e.g., "+a"). | |
| // 2. Name cannot start with a digit (e.g. "+123" is invalid). | |
| // 3. Must start with '+' or '-'. | |
| // 4. All characters after the sign must be alphanumeric or '_' or '-'. | |
| if (flag.size() < 2) { | |
| is_valid = false; | |
| ostream << "must have a name"; | |
| } else if (std::isdigit(static_cast<unsigned char>(flag[1]))) { | |
| is_valid = false; | |
| ostream << "name cannot start with a digit"; | |
| } else if (flag.front() != '+' && flag.front() != '-') { | |
| is_valid = false; | |
| ostream << "must start with '+' or '-'"; | |
| } else if (!std::all_of(flag.begin() + 1, flag.end(), [](unsigned char c) { | |
| return std::isalnum(c) || c == '_' || c == '-'; | |
| })) { | |
| is_valid = false; | |
| ostream << "contains invalid characters"; | |
| } | |
| if (!is_valid) { | |
| std::string message = | |
| ("feature flag '" + flag + "': " + warning_reason).str(); | |
| lldb_private::Debugger::ReportWarning(message, std::nullopt); | |
| continue; | |
| } | |
| valid_user_flags.push_back(flag.str()); | |
| if (flag.starts_with('-')) | |
| user_disabled_features.insert(flag.substr(1)); | |
| } | |
| // User feature string with only valid flags. | |
| llvm::SmallVector<std::string, 0> final_features; | |
| // Allow users to override default additional features. | |
| if (!subtarget_features.empty()) { | |
| for (llvm::StringRef flag : llvm::split(subtarget_features, ",")) { | |
| flag = flag.trim(); | |
| if (flag.empty()) | |
| continue; | |
| // By default, if both +flag and -flag are present in the feature string, | |
| // disassembler keeps the feature enabled (+flag). | |
| // To respect user intent, we make -flag(user) take priority over the | |
| // default +flag coming from ELF. | |
| bool add_flag = true; | |
| if (flag.size() >= 2 && flag.starts_with('+')) { | |
| llvm::StringRef feature_name = flag.substr(1); | |
| if (user_disabled_features.count(feature_name)) | |
| add_flag = false; | |
| } | |
| if (add_flag) | |
| final_features.push_back(flag.str()); | |
| } | |
| } | |
| // Append user flags. | |
| final_features.insert(final_features.end(), valid_user_flags.begin(), | |
| valid_user_flags.end()); | |
| user_feature_overrides = llvm::join(final_features, ","); | |
| } | |
| DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch, | |
| const char *flavor_string, | |
| const char *cpu_string, | |
| const char *features_string) | |
| : Disassembler(arch, flavor_string), m_exe_ctx(nullptr), m_inst(nullptr), | |
| m_data_from_file(false), m_adrp_address(LLDB_INVALID_ADDRESS), | |
| m_adrp_insn() { | |
| if (!FlavorValidForArchSpec(arch, m_flavor.c_str())) { | |
| m_flavor.assign("default"); | |
| } | |
| const bool cpu_or_features_overriden = cpu_string || features_string; | |
| unsigned flavor = ~0U; | |
| llvm::Triple triple = arch.GetTriple(); | |
| // So far the only supported flavor is "intel" on x86. The base class will | |
| // set this correctly coming in. | |
| if (triple.getArch() == llvm::Triple::x86 || | |
| triple.getArch() == llvm::Triple::x86_64) { | |
| if (m_flavor == "intel") { | |
| flavor = 1; | |
| } else if (m_flavor == "att") { | |
| flavor = 0; | |
| } | |
| } | |
| ArchSpec thumb_arch(arch); | |
| if (triple.getArch() == llvm::Triple::arm) { | |
| std::string thumb_arch_name(thumb_arch.GetTriple().getArchName().str()); | |
| // Replace "arm" with "thumb" so we get all thumb variants correct | |
| if (thumb_arch_name.size() > 3) { | |
| thumb_arch_name.erase(0, 3); | |
| thumb_arch_name.insert(0, "thumb"); | |
| } else { | |
| thumb_arch_name = "thumbv9.3a"; | |
| } | |
| thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name)); | |
| } | |
| // If no sub architecture specified then use the most recent arm architecture | |
| // so the disassembler will return all instructions. Without it we will see a | |
| // lot of unknown opcodes if the code uses instructions which are not | |
| // available in the oldest arm version (which is used when no sub architecture | |
| // is specified). | |
| if (triple.getArch() == llvm::Triple::arm && | |
| triple.getSubArch() == llvm::Triple::NoSubArch) | |
| triple.setArchName("armv9.3a"); | |
| std::string features_str = | |
| features_string ? std::string(features_string) : ""; | |
| const char *triple_str = triple.getTriple().c_str(); | |
| // ARM Cortex M0-M7 devices only execute thumb instructions | |
| if (arch.IsAlwaysThumbInstructions()) { | |
| triple_str = thumb_arch.GetTriple().getTriple().c_str(); | |
| if (!features_string) | |
| features_str += "+fp-armv8,"; | |
| } | |
| const char *cpu = cpu_string; | |
| if (!cpu_or_features_overriden) { | |
| switch (arch.GetCore()) { | |
| case ArchSpec::eCore_mips32: | |
| case ArchSpec::eCore_mips32el: | |
| cpu = "mips32"; | |
| break; | |
| case ArchSpec::eCore_mips32r2: | |
| case ArchSpec::eCore_mips32r2el: | |
| cpu = "mips32r2"; | |
| break; | |
| case ArchSpec::eCore_mips32r3: | |
| case ArchSpec::eCore_mips32r3el: | |
| cpu = "mips32r3"; | |
| break; | |
| case ArchSpec::eCore_mips32r5: | |
| case ArchSpec::eCore_mips32r5el: | |
| cpu = "mips32r5"; | |
| break; | |
| case ArchSpec::eCore_mips32r6: | |
| case ArchSpec::eCore_mips32r6el: | |
| cpu = "mips32r6"; | |
| break; | |
| case ArchSpec::eCore_mips64: | |
| case ArchSpec::eCore_mips64el: | |
| cpu = "mips64"; | |
| break; | |
| case ArchSpec::eCore_mips64r2: | |
| case ArchSpec::eCore_mips64r2el: | |
| cpu = "mips64r2"; | |
| break; | |
| case ArchSpec::eCore_mips64r3: | |
| case ArchSpec::eCore_mips64r3el: | |
| cpu = "mips64r3"; | |
| break; | |
| case ArchSpec::eCore_mips64r5: | |
| case ArchSpec::eCore_mips64r5el: | |
| cpu = "mips64r5"; | |
| break; | |
| case ArchSpec::eCore_mips64r6: | |
| case ArchSpec::eCore_mips64r6el: | |
| cpu = "mips64r6"; | |
| break; | |
| default: | |
| cpu = ""; | |
| break; | |
| } | |
| } | |
| if (arch.IsMIPS() && !cpu_or_features_overriden) { | |
| uint32_t arch_flags = arch.GetFlags(); | |
| if (arch_flags & ArchSpec::eMIPSAse_msa) | |
| features_str += "+msa,"; | |
| if (arch_flags & ArchSpec::eMIPSAse_dsp) | |
| features_str += "+dsp,"; | |
| if (arch_flags & ArchSpec::eMIPSAse_dspr2) | |
| features_str += "+dspr2,"; | |
| } | |
| // If any AArch64 variant, enable latest ISA with all extensions unless the | |
| // CPU or features were overridden. | |
| if (triple.isAArch64() && !cpu_or_features_overriden) { | |
| features_str += "+all,"; | |
| if (triple.getVendor() == llvm::Triple::Apple) | |
| cpu = "apple-latest"; | |
| } | |
| if (triple.isRISCV()) { | |
| auto subtarget_features = arch.GetSubtargetFeatures().getString(); | |
| if (!cpu_or_features_overriden) { | |
| if (!subtarget_features.empty()) { | |
| features_str += subtarget_features; | |
| } else { | |
| uint32_t arch_flags = arch.GetFlags(); | |
| if (arch_flags & ArchSpec::eRISCV_rvc) | |
| features_str += "+c,"; | |
| if (arch_flags & ArchSpec::eRISCV_rve) | |
| features_str += "+e,"; | |
| if ((arch_flags & ArchSpec::eRISCV_float_abi_single) == | |
| ArchSpec::eRISCV_float_abi_single) | |
| features_str += "+f,"; | |
| if ((arch_flags & ArchSpec::eRISCV_float_abi_double) == | |
| ArchSpec::eRISCV_float_abi_double) | |
| features_str += "+f,+d,"; | |
| if ((arch_flags & ArchSpec::eRISCV_float_abi_quad) == | |
| ArchSpec::eRISCV_float_abi_quad) | |
| features_str += "+f,+d,+q,"; | |
| // FIXME: how do we detect features such as `+a`, `+m`? | |
| // Turn them on by default now, since everyone seems to use them | |
| features_str += "+a,+m,"; | |
| } | |
| } else { | |
| // Merges default subtarget features with user overrides. | |
| UpdateSubtargetFeatures(subtarget_features, features_str); | |
| } | |
| } | |
| // We use m_disasm_up.get() to tell whether we are valid or not, so if this | |
| // isn't good for some reason, we won't be valid and FindPlugin will fail and | |
| // we won't get used. | |
| m_disasm_up = MCDisasmInstance::Create(triple_str, cpu, features_str.c_str(), | |
| flavor, *this); | |
| llvm::Triple::ArchType llvm_arch = triple.getArch(); | |
| // For arm CPUs that can execute arm or thumb instructions, also create a | |
| // thumb instruction disassembler. | |
| if (llvm_arch == llvm::Triple::arm) { | |
| std::string thumb_triple(thumb_arch.GetTriple().getTriple()); | |
| m_alternate_disasm_up = MCDisasmInstance::Create( | |
| thumb_triple.c_str(), "", features_str.c_str(), flavor, *this); | |
| if (!m_alternate_disasm_up) | |
| m_disasm_up.reset(); | |
| } else if (arch.IsMIPS()) { | |
| /* Create alternate disassembler for MIPS16 and microMIPS */ | |
| uint32_t arch_flags = arch.GetFlags(); | |
| if (arch_flags & ArchSpec::eMIPSAse_mips16) | |
| features_str += "+mips16,"; | |
| else if (arch_flags & ArchSpec::eMIPSAse_micromips) | |
| features_str += "+micromips,"; | |
| m_alternate_disasm_up = MCDisasmInstance::Create( | |
| triple_str, cpu, features_str.c_str(), flavor, *this); | |
| if (!m_alternate_disasm_up) | |
| m_disasm_up.reset(); | |
| } | |
| } | |
| DisassemblerLLVMC::~DisassemblerLLVMC() = default; | |
| lldb::DisassemblerSP DisassemblerLLVMC::CreateInstance(const ArchSpec &arch, | |
| const char *flavor, | |
| const char *cpu, | |
| const char *features) { | |
| if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) { | |
| auto disasm_sp = | |
| std::make_shared<DisassemblerLLVMC>(arch, flavor, cpu, features); | |
| if (disasm_sp && disasm_sp->IsValid()) | |
| return disasm_sp; | |
| } | |
| return lldb::DisassemblerSP(); | |
| } | |
| size_t DisassemblerLLVMC::DecodeInstructions(const Address &base_addr, | |
| const DataExtractor &data, | |
| lldb::offset_t data_offset, | |
| size_t num_instructions, | |
| bool append, bool data_from_file) { | |
| if (!append) | |
| m_instruction_list.Clear(); | |
| if (!IsValid()) | |
| return 0; | |
| m_data_from_file = data_from_file; | |
| uint32_t data_cursor = data_offset; | |
| const size_t data_byte_size = data.GetByteSize(); | |
| uint32_t instructions_parsed = 0; | |
| Address inst_addr(base_addr); | |
| while (data_cursor < data_byte_size && | |
| instructions_parsed < num_instructions) { | |
| AddressClass address_class = AddressClass::eCode; | |
| if (m_alternate_disasm_up) | |
| address_class = inst_addr.GetAddressClass(); | |
| InstructionSP inst_sp( | |
| new InstructionLLVMC(*this, inst_addr, address_class)); | |
| if (!inst_sp) | |
| break; | |
| uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); | |
| if (inst_size == 0) | |
| break; | |
| m_instruction_list.Append(inst_sp); | |
| data_cursor += inst_size; | |
| inst_addr.Slide(inst_size); | |
| instructions_parsed++; | |
| } | |
| return data_cursor - data_offset; | |
| } | |
| void DisassemblerLLVMC::Initialize() { | |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), | |
| "Disassembler that uses LLVM MC to disassemble " | |
| "i386, x86_64, ARM, and ARM64.", | |
| CreateInstance); | |
| llvm::InitializeAllTargetInfos(); | |
| llvm::InitializeAllTargetMCs(); | |
| llvm::InitializeAllAsmParsers(); | |
| llvm::InitializeAllDisassemblers(); | |
| } | |
| void DisassemblerLLVMC::Terminate() { | |
| PluginManager::UnregisterPlugin(CreateInstance); | |
| } | |
| int DisassemblerLLVMC::OpInfoCallback(void *disassembler, uint64_t pc, | |
| uint64_t offset, uint64_t size, | |
| int tag_type, void *tag_bug) { | |
| return static_cast<DisassemblerLLVMC *>(disassembler) | |
| ->OpInfo(pc, offset, size, tag_type, tag_bug); | |
| } | |
| const char *DisassemblerLLVMC::SymbolLookupCallback(void *disassembler, | |
| uint64_t value, | |
| uint64_t *type, uint64_t pc, | |
| const char **name) { | |
| return static_cast<DisassemblerLLVMC *>(disassembler) | |
| ->SymbolLookup(value, type, pc, name); | |
| } | |
| bool DisassemblerLLVMC::FlavorValidForArchSpec( | |
| const lldb_private::ArchSpec &arch, const char *flavor) { | |
| llvm::Triple triple = arch.GetTriple(); | |
| if (flavor == nullptr || strcmp(flavor, "default") == 0) | |
| return true; | |
| if (triple.getArch() == llvm::Triple::x86 || | |
| triple.getArch() == llvm::Triple::x86_64) { | |
| return strcmp(flavor, "intel") == 0 || strcmp(flavor, "att") == 0; | |
| } else | |
| return false; | |
| } | |
| bool DisassemblerLLVMC::IsValid() const { return m_disasm_up.operator bool(); } | |
| int DisassemblerLLVMC::OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size, | |
| int tag_type, void *tag_bug) { | |
| switch (tag_type) { | |
| default: | |
| break; | |
| case 1: | |
| memset(tag_bug, 0, sizeof(::LLVMOpInfo1)); | |
| break; | |
| } | |
| return 0; | |
| } | |
| const char *DisassemblerLLVMC::SymbolLookup(uint64_t value, uint64_t *type_ptr, | |
| uint64_t pc, const char **name) { | |
| if (*type_ptr) { | |
| if (m_exe_ctx && m_inst) { | |
| // std::string remove_this_prior_to_checkin; | |
| Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : nullptr; | |
| Address value_so_addr; | |
| Address pc_so_addr; | |
| if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64 || | |
| target->GetArchitecture().GetMachine() == llvm::Triple::aarch64_be || | |
| target->GetArchitecture().GetMachine() == llvm::Triple::aarch64_32) { | |
| if (*type_ptr == LLVMDisassembler_ReferenceType_In_ARM64_ADRP) { | |
| m_adrp_address = pc; | |
| m_adrp_insn = value; | |
| *name = nullptr; | |
| *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; | |
| return nullptr; | |
| } | |
| // If this instruction is an ADD and | |
| // the previous instruction was an ADRP and | |
| // the ADRP's register and this ADD's register are the same, | |
| // then this is a pc-relative address calculation. | |
| if (*type_ptr == LLVMDisassembler_ReferenceType_In_ARM64_ADDXri && | |
| m_adrp_insn && m_adrp_address == pc - 4 && | |
| (*m_adrp_insn & 0x1f) == ((value >> 5) & 0x1f)) { | |
| uint32_t addxri_inst; | |
| uint64_t adrp_imm, addxri_imm; | |
| // Get immlo and immhi bits, OR them together to get the ADRP imm | |
| // value. | |
| adrp_imm = | |
| ((*m_adrp_insn & 0x00ffffe0) >> 3) | ((*m_adrp_insn >> 29) & 0x3); | |
| // if high bit of immhi after right-shifting set, sign extend | |
| if (adrp_imm & (1ULL << 20)) | |
| adrp_imm |= ~((1ULL << 21) - 1); | |
| addxri_inst = value; | |
| addxri_imm = (addxri_inst >> 10) & 0xfff; | |
| // check if 'sh' bit is set, shift imm value up if so | |
| // (this would make no sense, ADRP already gave us this part) | |
| if ((addxri_inst >> (12 + 5 + 5)) & 1) | |
| addxri_imm <<= 12; | |
| value = (m_adrp_address & 0xfffffffffffff000LL) + (adrp_imm << 12) + | |
| addxri_imm; | |
| } | |
| m_adrp_address = LLDB_INVALID_ADDRESS; | |
| m_adrp_insn.reset(); | |
| } | |
| if (m_inst->UsingFileAddress()) { | |
| ModuleSP module_sp(m_inst->GetAddress().GetModule()); | |
| if (module_sp) { | |
| module_sp->ResolveFileAddress(value, value_so_addr); | |
| module_sp->ResolveFileAddress(pc, pc_so_addr); | |
| } | |
| } else if (target && target->HasLoadedSections()) { | |
| target->ResolveLoadAddress(value, value_so_addr); | |
| target->ResolveLoadAddress(pc, pc_so_addr); | |
| } | |
| SymbolContext sym_ctx; | |
| const SymbolContextItem resolve_scope = | |
| eSymbolContextFunction | eSymbolContextSymbol; | |
| if (pc_so_addr.IsValid() && pc_so_addr.GetModule()) { | |
| pc_so_addr.GetModule()->ResolveSymbolContextForAddress( | |
| pc_so_addr, resolve_scope, sym_ctx); | |
| } | |
| if (value_so_addr.IsValid() && value_so_addr.GetSection()) { | |
| StreamString ss; | |
| bool format_omitting_current_func_name = false; | |
| if (sym_ctx.symbol || sym_ctx.function) { | |
| AddressRange range; | |
| for (uint32_t idx = 0; | |
| sym_ctx.GetAddressRange(resolve_scope, idx, false, range); | |
| ++idx) { | |
| if (range.ContainsLoadAddress(value_so_addr, target)) { | |
| format_omitting_current_func_name = true; | |
| break; | |
| } | |
| } | |
| } | |
| // If the "value" address (the target address we're symbolicating) is | |
| // inside the same SymbolContext as the current instruction pc | |
| // (pc_so_addr), don't print the full function name - just print it | |
| // with DumpStyleNoFunctionName style, e.g. "<+36>". | |
| if (format_omitting_current_func_name) { | |
| value_so_addr.Dump(&ss, target, Address::DumpStyleNoFunctionName, | |
| Address::DumpStyleSectionNameOffset); | |
| } else { | |
| value_so_addr.Dump( | |
| &ss, target, | |
| Address::DumpStyleResolvedDescriptionNoFunctionArguments, | |
| Address::DumpStyleSectionNameOffset); | |
| } | |
| if (!ss.GetString().empty()) { | |
| // If Address::Dump returned a multi-line description, most commonly | |
| // seen when we have multiple levels of inlined functions at an | |
| // address, only show the first line. | |
| std::string str = std::string(ss.GetString()); | |
| size_t first_eol_char = str.find_first_of("\r\n"); | |
| if (first_eol_char != std::string::npos) { | |
| str.erase(first_eol_char); | |
| } | |
| m_inst->AppendComment(str); | |
| } | |
| } | |
| } | |
| } | |
| // TODO: llvm-objdump sets the type_ptr to the | |
| // LLVMDisassembler_ReferenceType_Out_* values | |
| // based on where value_so_addr is pointing, with | |
| // Mach-O specific augmentations in MachODump.cpp. e.g. | |
| // see what AArch64ExternalSymbolizer::tryAddingSymbolicOperand | |
| // handles. | |
| *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; | |
| *name = nullptr; | |
| return nullptr; | |
| } |