blob: f0cb7be70210d750068efaa060bd8b016569921e [file] [log] [blame]
//===-- 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