1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
//===-- 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 <cstdint>
#include <optional>
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<size_t>(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<protocol::Source> 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<DisassembleResponseBody>
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<DAPError>(
"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<DAPError>(
"Unexpected error while disassembling instructions.");
lldb::SBInstructionList insts = dap.target.ReadInstructions(
disassemble_start_addr, args.instructionCount);
if (!insts.IsValid())
return llvm::make_error<DAPError>(
"Unexpected error while disassembling instructions.");
// Convert the found instructions to the DAP format.
const bool resolve_symbols = args.resolveSymbols;
std::vector<DisassembledInstruction> 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<size_t>(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<DisassembledInstruction> 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
|