1//===-- DisassembleRequestHandler.cpp -------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "EventHelper.h"
11#include "JSONUtils.h"
12#include "LLDBUtils.h"
13#include "Protocol/ProtocolRequests.h"
14#include "Protocol/ProtocolTypes.h"
15#include "ProtocolUtils.h"
16#include "RequestHandler.h"
17#include "lldb/API/SBAddress.h"
18#include "lldb/API/SBInstruction.h"
19#include "lldb/API/SBLineEntry.h"
20#include "lldb/API/SBTarget.h"
21#include "lldb/lldb-types.h"
22#include "llvm/ADT/StringExtras.h"
23#include "llvm/Support/Error.h"
24#include <cstdint>
25#include <optional>
26
27using namespace lldb_dap::protocol;
28
29namespace lldb_dap {
30
31static protocol::DisassembledInstruction GetInvalidInstruction() {
32 DisassembledInstruction invalid_inst;
33 invalid_inst.address = LLDB_INVALID_ADDRESS;
34 invalid_inst.presentationHint =
35 DisassembledInstruction::eDisassembledInstructionPresentationHintInvalid;
36 return invalid_inst;
37}
38
39static lldb::SBAddress GetDisassembleStartAddress(lldb::SBTarget target,
40 lldb::SBAddress addr,
41 int64_t instruction_offset) {
42 if (instruction_offset == 0)
43 return addr;
44
45 if (target.GetMinimumOpcodeByteSize() == target.GetMaximumOpcodeByteSize()) {
46 // We have fixed opcode size, so we can calculate the address directly,
47 // negative or positive.
48 lldb::addr_t load_addr = addr.GetLoadAddress(target);
49 load_addr += instruction_offset * target.GetMinimumOpcodeByteSize();
50 return lldb::SBAddress(load_addr, target);
51 }
52
53 if (instruction_offset > 0) {
54 lldb::SBInstructionList forward_insts =
55 target.ReadInstructions(base_addr: addr, count: instruction_offset + 1);
56 return forward_insts.GetInstructionAtIndex(idx: forward_insts.GetSize() - 1)
57 .GetAddress();
58 }
59
60 // We have a negative instruction offset, so we need to disassemble backwards.
61 // The opcode size is not fixed, so we have no idea where to start from.
62 // Let's try from the start of the current symbol if available.
63 auto symbol = addr.GetSymbol();
64 if (!symbol.IsValid())
65 return addr;
66
67 // Add valid instructions before the current instruction using the symbol.
68 lldb::SBInstructionList symbol_insts =
69 target.ReadInstructions(start_addr: symbol.GetStartAddress(), end_addr: addr, flavor_string: nullptr);
70 if (!symbol_insts.IsValid() || symbol_insts.GetSize() == 0)
71 return addr;
72
73 const auto backwards_instructions_count =
74 static_cast<size_t>(std::abs(i: instruction_offset));
75 if (symbol_insts.GetSize() < backwards_instructions_count) {
76 // We don't have enough instructions to disassemble backwards, so just
77 // return the start address of the symbol.
78 return symbol_insts.GetInstructionAtIndex(idx: 0).GetAddress();
79 }
80
81 return symbol_insts
82 .GetInstructionAtIndex(idx: symbol_insts.GetSize() -
83 backwards_instructions_count)
84 .GetAddress();
85}
86
87static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction(
88 lldb::SBTarget &target, lldb::SBInstruction &inst, bool resolve_symbols) {
89 if (!inst.IsValid())
90 return GetInvalidInstruction();
91
92 auto addr = inst.GetAddress();
93 const auto inst_addr = addr.GetLoadAddress(target);
94
95 // FIXME: This is a workaround - this address might come from
96 // disassembly that started in a different section, and thus
97 // comparisons between this object and other address objects with the
98 // same load address will return false.
99 addr = lldb::SBAddress(inst_addr, target);
100
101 const char *m = inst.GetMnemonic(target);
102 const char *o = inst.GetOperands(target);
103 const char *c = inst.GetComment(target);
104 auto d = inst.GetData(target);
105
106 std::string bytes;
107 llvm::raw_string_ostream sb(bytes);
108 for (unsigned i = 0; i < inst.GetByteSize(); i++) {
109 lldb::SBError error;
110 uint8_t b = d.GetUnsignedInt8(error, offset: i);
111 if (error.Success())
112 sb << llvm::format(Fmt: "%2.2x ", Vals: b);
113 }
114
115 DisassembledInstruction disassembled_inst;
116 disassembled_inst.address = inst_addr;
117 disassembled_inst.instructionBytes =
118 bytes.size() > 0 ? bytes.substr(pos: 0, n: bytes.size() - 1) : "";
119
120 std::string instruction;
121 llvm::raw_string_ostream si(instruction);
122
123 lldb::SBSymbol symbol = addr.GetSymbol();
124 // Only add the symbol on the first line of the function.
125 if (symbol.IsValid() && symbol.GetStartAddress() == addr) {
126 // If we have a valid symbol, append it as a label prefix for the first
127 // instruction. This is so you can see the start of a function/callsite
128 // in the assembly, at the moment VS Code (1.80) does not visualize the
129 // symbol associated with the assembly instruction.
130 si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName()
131 : symbol.GetName())
132 << ": ";
133
134 if (resolve_symbols)
135 disassembled_inst.symbol = symbol.GetDisplayName();
136 }
137
138 si << llvm::formatv(Fmt: "{0,7} {1,12}", Vals&: m, Vals&: o);
139 if (c && c[0]) {
140 si << " ; " << c;
141 }
142
143 disassembled_inst.instruction = std::move(instruction);
144
145 protocol::Source source = CreateSource(address: addr, target);
146 lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address: addr);
147
148 // If the line number is 0 then the entry represents a compiler generated
149 // location.
150 if (!IsAssemblySource(source) && line_entry.GetStartAddress() == addr &&
151 line_entry.IsValid() && line_entry.GetFileSpec().IsValid() &&
152 line_entry.GetLine() != 0) {
153
154 disassembled_inst.location = std::move(source);
155 const auto line = line_entry.GetLine();
156 if (line != 0 && line != LLDB_INVALID_LINE_NUMBER)
157 disassembled_inst.line = line;
158
159 const auto column = line_entry.GetColumn();
160 if (column != 0 && column != LLDB_INVALID_COLUMN_NUMBER)
161 disassembled_inst.column = column;
162
163 lldb::SBAddress end_addr = line_entry.GetEndAddress();
164 auto end_line_entry = GetLineEntryForAddress(target, address: end_addr);
165 if (end_line_entry.IsValid() &&
166 end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) {
167 const auto end_line = end_line_entry.GetLine();
168 if (end_line != 0 && end_line != LLDB_INVALID_LINE_NUMBER &&
169 end_line != line) {
170 disassembled_inst.endLine = end_line;
171
172 const auto end_column = end_line_entry.GetColumn();
173 if (end_column != 0 && end_column != LLDB_INVALID_COLUMN_NUMBER &&
174 end_column != column)
175 disassembled_inst.endColumn = end_column - 1;
176 }
177 }
178 }
179
180 return disassembled_inst;
181}
182
183/// Disassembles code stored at the provided location.
184/// Clients should only call this request if the corresponding capability
185/// `supportsDisassembleRequest` is true.
186llvm::Expected<DisassembleResponseBody>
187DisassembleRequestHandler::Run(const DisassembleArguments &args) const {
188 std::optional<lldb::addr_t> addr_opt =
189 DecodeMemoryReference(memoryReference: args.memoryReference);
190 if (!addr_opt.has_value())
191 return llvm::make_error<DAPError>(Args: "Malformed memory reference: " +
192 args.memoryReference);
193
194 lldb::addr_t addr_ptr = *addr_opt;
195 addr_ptr += args.offset.value_or(u: 0);
196 lldb::SBAddress addr(addr_ptr, dap.target);
197 if (!addr.IsValid())
198 return llvm::make_error<DAPError>(
199 Args: "Memory reference not found in the current binary.");
200
201 // Offset (in instructions) to be applied after the byte offset (if any)
202 // before disassembling. Can be negative.
203 int64_t instruction_offset = args.instructionOffset.value_or(u: 0);
204
205 // Calculate a sufficient address to start disassembling from.
206 lldb::SBAddress disassemble_start_addr =
207 GetDisassembleStartAddress(target: dap.target, addr, instruction_offset);
208 if (!disassemble_start_addr.IsValid())
209 return llvm::make_error<DAPError>(
210 Args: "Unexpected error while disassembling instructions.");
211
212 lldb::SBInstructionList insts = dap.target.ReadInstructions(
213 base_addr: disassemble_start_addr, count: args.instructionCount);
214 if (!insts.IsValid())
215 return llvm::make_error<DAPError>(
216 Args: "Unexpected error while disassembling instructions.");
217
218 // Conver the found instructions to the DAP format.
219 const bool resolve_symbols = args.resolveSymbols.value_or(u: false);
220 std::vector<DisassembledInstruction> instructions;
221 size_t original_address_index = args.instructionCount;
222 for (size_t i = 0; i < insts.GetSize(); ++i) {
223 lldb::SBInstruction inst = insts.GetInstructionAtIndex(idx: i);
224 if (inst.GetAddress() == addr)
225 original_address_index = i;
226
227 instructions.push_back(x: ConvertSBInstructionToDisassembledInstruction(
228 target&: dap.target, inst, resolve_symbols));
229 }
230
231 // Check if we miss instructions at the beginning.
232 if (instruction_offset < 0) {
233 const auto backwards_instructions_count =
234 static_cast<size_t>(std::abs(i: instruction_offset));
235 if (original_address_index < backwards_instructions_count) {
236 // We don't have enough instructions before the main address as was
237 // requested. Let's pad the start of the instructions with invalid
238 // instructions.
239 std::vector<DisassembledInstruction> invalid_instructions(
240 backwards_instructions_count - original_address_index,
241 GetInvalidInstruction());
242 instructions.insert(position: instructions.begin(), first: invalid_instructions.begin(),
243 last: invalid_instructions.end());
244
245 // Trim excess instructions if needed.
246 if (instructions.size() > args.instructionCount)
247 instructions.resize(new_size: args.instructionCount);
248 }
249 }
250
251 // Pad the instructions with invalid instructions if needed.
252 while (instructions.size() < args.instructionCount) {
253 instructions.push_back(x: GetInvalidInstruction());
254 }
255
256 return DisassembleResponseBody{.instructions: std::move(instructions)};
257}
258
259} // namespace lldb_dap
260

source code of lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp