//===-- 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 "RequestHandler.h" #include "lldb/API/SBInstruction.h" #include "llvm/ADT/StringExtras.h" namespace lldb_dap { // "DisassembleRequest": { // "allOf": [ { "$ref": "#/definitions/Request" }, { // "type": "object", // "description": "Disassembles code stored at the provided // location.\nClients should only call this request if the corresponding // capability `supportsDisassembleRequest` is true.", "properties": { // "command": { // "type": "string", // "enum": [ "disassemble" ] // }, // "arguments": { // "$ref": "#/definitions/DisassembleArguments" // } // }, // "required": [ "command", "arguments" ] // }] // }, // "DisassembleArguments": { // "type": "object", // "description": "Arguments for `disassemble` request.", // "properties": { // "memoryReference": { // "type": "string", // "description": "Memory reference to the base location containing the // instructions to disassemble." // }, // "offset": { // "type": "integer", // "description": "Offset (in bytes) to be applied to the reference // location before disassembling. Can be negative." // }, // "instructionOffset": { // "type": "integer", // "description": "Offset (in instructions) to be applied after the byte // offset (if any) before disassembling. Can be negative." // }, // "instructionCount": { // "type": "integer", // "description": "Number of instructions to disassemble starting at the // specified location and offset.\nAn adapter must return exactly this // number of instructions - any unavailable instructions should be // replaced with an implementation-defined 'invalid instruction' value." // }, // "resolveSymbols": { // "type": "boolean", // "description": "If true, the adapter should attempt to resolve memory // addresses and other values to symbolic names." // } // }, // "required": [ "memoryReference", "instructionCount" ] // }, // "DisassembleResponse": { // "allOf": [ { "$ref": "#/definitions/Response" }, { // "type": "object", // "description": "Response to `disassemble` request.", // "properties": { // "body": { // "type": "object", // "properties": { // "instructions": { // "type": "array", // "items": { // "$ref": "#/definitions/DisassembledInstruction" // }, // "description": "The list of disassembled instructions." // } // }, // "required": [ "instructions" ] // } // } // }] // } void DisassembleRequestHandler::operator()( const llvm::json::Object &request) const { llvm::json::Object response; FillResponse(request, response); auto *arguments = request.getObject("arguments"); llvm::StringRef memoryReference = GetString(arguments, "memoryReference").value_or(""); auto addr_opt = DecodeMemoryReference(memoryReference); if (!addr_opt.has_value()) { response["success"] = false; response["message"] = "Malformed memory reference: " + memoryReference.str(); dap.SendJSON(llvm::json::Value(std::move(response))); return; } lldb::addr_t addr_ptr = *addr_opt; addr_ptr += GetInteger(arguments, "instructionOffset").value_or(0); lldb::SBAddress addr(addr_ptr, dap.target); if (!addr.IsValid()) { response["success"] = false; response["message"] = "Memory reference not found in the current binary."; dap.SendJSON(llvm::json::Value(std::move(response))); return; } const auto inst_count = GetInteger(arguments, "instructionCount").value_or(0); lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count); if (!insts.IsValid()) { response["success"] = false; response["message"] = "Failed to find instructions for memory address."; dap.SendJSON(llvm::json::Value(std::move(response))); return; } const bool resolveSymbols = GetBoolean(arguments, "resolveSymbols").value_or(false); llvm::json::Array instructions; const auto num_insts = insts.GetSize(); for (size_t i = 0; i < num_insts; ++i) { lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); auto addr = inst.GetAddress(); const auto inst_addr = addr.GetLoadAddress(dap.target); const char *m = inst.GetMnemonic(dap.target); const char *o = inst.GetOperands(dap.target); const char *c = inst.GetComment(dap.target); auto d = inst.GetData(dap.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); } } llvm::json::Object disassembled_inst{ {"address", "0x" + llvm::utohexstr(inst_addr)}, {"instructionBytes", bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""}, }; std::string instruction; llvm::raw_string_ostream si(instruction); lldb::SBSymbol symbol = addr.GetSymbol(); // Only add the symbol on the first line of the function. if (symbol.IsValid() && symbol.GetStartAddress() == addr) { // If we have a valid symbol, append it as a label prefix for the first // instruction. This is so you can see the start of a function/callsite // in the assembly, at the moment VS Code (1.80) does not visualize the // symbol associated with the assembly instruction. si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName() : symbol.GetName()) << ": "; if (resolveSymbols) { disassembled_inst.try_emplace("symbol", symbol.GetDisplayName()); } } si << llvm::formatv("{0,7} {1,12}", m, o); if (c && c[0]) { si << " ; " << c; } disassembled_inst.try_emplace("instruction", instruction); auto line_entry = addr.GetLineEntry(); // If the line number is 0 then the entry represents a compiler generated // location. if (line_entry.GetStartAddress() == addr && line_entry.IsValid() && line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) { auto source = CreateSource(line_entry); disassembled_inst.try_emplace("location", source); const auto line = line_entry.GetLine(); if (line && line != LLDB_INVALID_LINE_NUMBER) { disassembled_inst.try_emplace("line", line); } const auto column = line_entry.GetColumn(); if (column && column != LLDB_INVALID_COLUMN_NUMBER) { disassembled_inst.try_emplace("column", column); } auto end_line_entry = line_entry.GetEndAddress().GetLineEntry(); if (end_line_entry.IsValid() && end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) { const auto end_line = end_line_entry.GetLine(); if (end_line && end_line != LLDB_INVALID_LINE_NUMBER && end_line != line) { disassembled_inst.try_emplace("endLine", end_line); const auto end_column = end_line_entry.GetColumn(); if (end_column && end_column != LLDB_INVALID_COLUMN_NUMBER && end_column != column) { disassembled_inst.try_emplace("endColumn", end_column - 1); } } } } instructions.emplace_back(std::move(disassembled_inst)); } llvm::json::Object body; body.try_emplace("instructions", std::move(instructions)); response.try_emplace("body", std::move(body)); dap.SendJSON(llvm::json::Value(std::move(response))); } } // namespace lldb_dap