| //===-- 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<int64_t>(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<int64_t>(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 |