//===-- 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 #include 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(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 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 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( "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( "Unexpected error while disassembling instructions."); lldb::SBInstructionList insts = dap.target.ReadInstructions( disassemble_start_addr, args.instructionCount); if (!insts.IsValid()) return llvm::make_error( "Unexpected error while disassembling instructions."); // Convert the found instructions to the DAP format. const bool resolve_symbols = args.resolveSymbols; std::vector 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(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 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