| //===-- DisassembleRequestHandler.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 "DAP.h" |
| #include "EventHelper.h" |
| #include "JSONUtils.h" |
| #include "LLDBUtils.h" |
| #include "Protocol/ProtocolRequests.h" |
| #include "Protocol/ProtocolTypes.h" |
| #include "ProtocolUtils.h" |
| #include "RequestHandler.h" |
| #include "lldb/API/SBAddress.h" |
| #include "lldb/API/SBInstruction.h" |
| #include "lldb/API/SBLineEntry.h" |
| #include "lldb/API/SBTarget.h" |
| #include "lldb/lldb-types.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/Error.h" |
| #include <cstdint> |
| #include <optional> |
| |
| using namespace lldb_dap::protocol; |
| |
| namespace lldb_dap { |
| |
| static protocol::DisassembledInstruction GetInvalidInstruction() { |
| DisassembledInstruction invalid_inst; |
| invalid_inst.address = LLDB_INVALID_ADDRESS; |
| invalid_inst.presentationHint = |
| DisassembledInstruction::eDisassembledInstructionPresentationHintInvalid; |
| return invalid_inst; |
| } |
| |
| static lldb::SBAddress GetDisassembleStartAddress(lldb::SBTarget target, |
| lldb::SBAddress addr, |
| int64_t instruction_offset) { |
| if (instruction_offset == 0) |
| return addr; |
| |
| if (target.GetMinimumOpcodeByteSize() == target.GetMaximumOpcodeByteSize()) { |
| // We have fixed opcode size, so we can calculate the address directly, |
| // negative or positive. |
| lldb::addr_t load_addr = addr.GetLoadAddress(target); |
| load_addr += instruction_offset * target.GetMinimumOpcodeByteSize(); |
| return lldb::SBAddress(load_addr, target); |
| } |
| |
| if (instruction_offset > 0) { |
| lldb::SBInstructionList forward_insts = |
| target.ReadInstructions(addr, instruction_offset + 1); |
| return forward_insts.GetInstructionAtIndex(forward_insts.GetSize() - 1) |
| .GetAddress(); |
| } |
| |
| // We have a negative instruction offset, so we need to disassemble backwards. |
| // The opcode size is not fixed, so we have no idea where to start from. |
| // Let's try from the start of the current symbol if available. |
| auto symbol = addr.GetSymbol(); |
| if (!symbol.IsValid()) |
| return addr; |
| |
| // Add valid instructions before the current instruction using the symbol. |
| lldb::SBInstructionList symbol_insts = |
| target.ReadInstructions(symbol.GetStartAddress(), addr, nullptr); |
| if (!symbol_insts.IsValid() || symbol_insts.GetSize() == 0) |
| return addr; |
| |
| const auto backwards_instructions_count = |
| static_cast<size_t>(std::abs(instruction_offset)); |
| if (symbol_insts.GetSize() < backwards_instructions_count) { |
| // We don't have enough instructions to disassemble backwards, so just |
| // return the start address of the symbol. |
| return symbol_insts.GetInstructionAtIndex(0).GetAddress(); |
| } |
| |
| return symbol_insts |
| .GetInstructionAtIndex(symbol_insts.GetSize() - |
| backwards_instructions_count) |
| .GetAddress(); |
| } |
| |
| static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction( |
| DAP &dap, lldb::SBInstruction &inst, bool resolve_symbols) { |
| lldb::SBTarget target = dap.target; |
| if (!inst.IsValid()) |
| return GetInvalidInstruction(); |
| |
| auto addr = inst.GetAddress(); |
| const auto inst_addr = addr.GetLoadAddress(target); |
| |
| // FIXME: This is a workaround - this address might come from |
| // disassembly that started in a different section, and thus |
| // comparisons between this object and other address objects with the |
| // same load address will return false. |
| addr = lldb::SBAddress(inst_addr, target); |
| |
| const char *m = inst.GetMnemonic(target); |
| const char *o = inst.GetOperands(target); |
| std::string c = inst.GetComment(target); |
| auto d = inst.GetData(target); |
| |
| std::string bytes; |
| llvm::raw_string_ostream sb(bytes); |
| for (unsigned i = 0; i < inst.GetByteSize(); i++) { |
| lldb::SBError error; |
| uint8_t b = d.GetUnsignedInt8(error, i); |
| if (error.Success()) |
| sb << llvm::format("%2.2x ", b); |
| } |
| |
| DisassembledInstruction disassembled_inst; |
| disassembled_inst.address = inst_addr; |
| |
| if (!bytes.empty()) // remove last whitespace |
| bytes.pop_back(); |
| disassembled_inst.instructionBytes = std::move(bytes); |
| |
| llvm::raw_string_ostream si(disassembled_inst.instruction); |
| si << llvm::formatv("{0,-7} {1,-25}", m, o); |
| |
| // Only add the symbol on the first line of the function. |
| // in the comment section |
| if (lldb::SBSymbol symbol = addr.GetSymbol(); |
| symbol.GetStartAddress() == addr) { |
| const llvm::StringRef sym_display_name = symbol.GetDisplayName(); |
| c.append(" "); |
| c.append(sym_display_name); |
| |
| if (resolve_symbols) |
| disassembled_inst.symbol = sym_display_name; |
| } |
| |
| if (!c.empty()) { |
| si << " ; " << c; |
| } |
| |
| std::optional<protocol::Source> source = dap.ResolveSource(addr); |
| lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, addr); |
| |
| // If the line number is 0 then the entry represents a compiler generated |
| // location. |
| if (source && !IsAssemblySource(*source) && |
| line_entry.GetStartAddress() == addr && line_entry.IsValid() && |
| line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) { |
| |
| disassembled_inst.location = std::move(source); |
| const auto line = line_entry.GetLine(); |
| if (line != 0 && line != LLDB_INVALID_LINE_NUMBER) |
| disassembled_inst.line = line; |
| |
| const auto column = line_entry.GetColumn(); |
| if (column != 0 && column != LLDB_INVALID_COLUMN_NUMBER) |
| disassembled_inst.column = column; |
| |
| lldb::SBAddress end_addr = line_entry.GetEndAddress(); |
| auto end_line_entry = GetLineEntryForAddress(target, end_addr); |
| if (end_line_entry.IsValid() && |
| end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) { |
| const auto end_line = end_line_entry.GetLine(); |
| if (end_line != 0 && end_line != LLDB_INVALID_LINE_NUMBER && |
| end_line != line) { |
| disassembled_inst.endLine = end_line; |
| |
| const auto end_column = end_line_entry.GetColumn(); |
| if (end_column != 0 && end_column != LLDB_INVALID_COLUMN_NUMBER && |
| end_column != column) |
| disassembled_inst.endColumn = end_column - 1; |
| } |
| } |
| } |
| |
| return disassembled_inst; |
| } |
| |
| /// Disassembles code stored at the provided location. |
| /// Clients should only call this request if the corresponding capability |
| /// `supportsDisassembleRequest` is true. |
| llvm::Expected<DisassembleResponseBody> |
| DisassembleRequestHandler::Run(const DisassembleArguments &args) const { |
| const lldb::addr_t addr_ptr = args.memoryReference + args.offset; |
| lldb::SBAddress addr(addr_ptr, dap.target); |
| if (!addr.IsValid()) |
| return llvm::make_error<DAPError>( |
| "Memory reference not found in the current binary."); |
| |
| // Offset (in instructions) to be applied after the byte offset (if any) |
| // before disassembling. Can be negative. |
| const int64_t instruction_offset = args.instructionOffset; |
| |
| // Calculate a sufficient address to start disassembling from. |
| lldb::SBAddress disassemble_start_addr = |
| GetDisassembleStartAddress(dap.target, addr, instruction_offset); |
| if (!disassemble_start_addr.IsValid()) |
| return llvm::make_error<DAPError>( |
| "Unexpected error while disassembling instructions."); |
| |
| lldb::SBInstructionList insts = dap.target.ReadInstructions( |
| disassemble_start_addr, args.instructionCount); |
| if (!insts.IsValid()) |
| return llvm::make_error<DAPError>( |
| "Unexpected error while disassembling instructions."); |
| |
| // Convert the found instructions to the DAP format. |
| const bool resolve_symbols = args.resolveSymbols; |
| std::vector<DisassembledInstruction> instructions; |
| size_t original_address_index = args.instructionCount; |
| for (size_t i = 0; i < insts.GetSize(); ++i) { |
| lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); |
| if (inst.GetAddress() == addr) |
| original_address_index = i; |
| |
| instructions.push_back(ConvertSBInstructionToDisassembledInstruction( |
| dap, inst, resolve_symbols)); |
| } |
| |
| // Check if we miss instructions at the beginning. |
| if (instruction_offset < 0) { |
| const auto backwards_instructions_count = |
| static_cast<size_t>(std::abs(instruction_offset)); |
| if (original_address_index < backwards_instructions_count) { |
| // We don't have enough instructions before the main address as was |
| // requested. Let's pad the start of the instructions with invalid |
| // instructions. |
| std::vector<DisassembledInstruction> invalid_instructions( |
| backwards_instructions_count - original_address_index, |
| GetInvalidInstruction()); |
| instructions.insert(instructions.begin(), invalid_instructions.begin(), |
| invalid_instructions.end()); |
| |
| // Trim excess instructions if needed. |
| if (instructions.size() > args.instructionCount) |
| instructions.resize(args.instructionCount); |
| } |
| } |
| |
| // Pad the instructions with invalid instructions if needed. |
| while (instructions.size() < args.instructionCount) { |
| instructions.push_back(GetInvalidInstruction()); |
| } |
| |
| return DisassembleResponseBody{std::move(instructions)}; |
| } |
| |
| } // namespace lldb_dap |