1 //===-- CommandObjectDisassemble.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 "CommandObjectDisassemble.h"
10 #include "lldb/Core/AddressRange.h"
11 #include "lldb/Core/Disassembler.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Host/OptionParser.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Interpreter/CommandReturnObject.h"
16 #include "lldb/Interpreter/OptionArgParser.h"
17 #include "lldb/Interpreter/Options.h"
18 #include "lldb/Symbol/Function.h"
19 #include "lldb/Symbol/Symbol.h"
20 #include "lldb/Target/SectionLoadList.h"
21 #include "lldb/Target/StackFrame.h"
22 #include "lldb/Target/Target.h"
23
24 static constexpr unsigned default_disasm_byte_size = 32;
25 static constexpr unsigned default_disasm_num_ins = 4;
26
27 using namespace lldb;
28 using namespace lldb_private;
29
30 #define LLDB_OPTIONS_disassemble
31 #include "CommandOptions.inc"
32
CommandOptions()33 CommandObjectDisassemble::CommandOptions::CommandOptions()
34 : Options(), func_name(), plugin_name(), flavor_string(), arch() {
35 OptionParsingStarting(nullptr);
36 }
37
38 CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
39
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)40 Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
41 uint32_t option_idx, llvm::StringRef option_arg,
42 ExecutionContext *execution_context) {
43 Status error;
44
45 const int short_option = m_getopt_table[option_idx].val;
46
47 switch (short_option) {
48 case 'm':
49 show_mixed = true;
50 break;
51
52 case 'C':
53 if (option_arg.getAsInteger(0, num_lines_context))
54 error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
55 option_arg.str().c_str());
56 break;
57
58 case 'c':
59 if (option_arg.getAsInteger(0, num_instructions))
60 error.SetErrorStringWithFormat(
61 "invalid num of instructions string: \"%s\"",
62 option_arg.str().c_str());
63 break;
64
65 case 'b':
66 show_bytes = true;
67 break;
68
69 case 's': {
70 start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
71 LLDB_INVALID_ADDRESS, &error);
72 if (start_addr != LLDB_INVALID_ADDRESS)
73 some_location_specified = true;
74 } break;
75 case 'e': {
76 end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
77 LLDB_INVALID_ADDRESS, &error);
78 if (end_addr != LLDB_INVALID_ADDRESS)
79 some_location_specified = true;
80 } break;
81
82 case 'n':
83 func_name.assign(std::string(option_arg));
84 some_location_specified = true;
85 break;
86
87 case 'p':
88 at_pc = true;
89 some_location_specified = true;
90 break;
91
92 case 'l':
93 frame_line = true;
94 // Disassemble the current source line kind of implies showing mixed source
95 // code context.
96 show_mixed = true;
97 some_location_specified = true;
98 break;
99
100 case 'P':
101 plugin_name.assign(std::string(option_arg));
102 break;
103
104 case 'F': {
105 TargetSP target_sp =
106 execution_context ? execution_context->GetTargetSP() : TargetSP();
107 if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
108 llvm::Triple::x86 ||
109 target_sp->GetArchitecture().GetTriple().getArch() ==
110 llvm::Triple::x86_64)) {
111 flavor_string.assign(std::string(option_arg));
112 } else
113 error.SetErrorStringWithFormat("Disassembler flavors are currently only "
114 "supported for x86 and x86_64 targets.");
115 break;
116 }
117
118 case 'r':
119 raw = true;
120 break;
121
122 case 'f':
123 current_function = true;
124 some_location_specified = true;
125 break;
126
127 case 'A':
128 if (execution_context) {
129 const auto &target_sp = execution_context->GetTargetSP();
130 auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
131 arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
132 }
133 break;
134
135 case 'a': {
136 symbol_containing_addr = OptionArgParser::ToAddress(
137 execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
138 if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
139 some_location_specified = true;
140 }
141 } break;
142
143 case '\x01':
144 force = true;
145 break;
146
147 default:
148 llvm_unreachable("Unimplemented option");
149 }
150
151 return error;
152 }
153
OptionParsingStarting(ExecutionContext * execution_context)154 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
155 ExecutionContext *execution_context) {
156 show_mixed = false;
157 show_bytes = false;
158 num_lines_context = 0;
159 num_instructions = 0;
160 func_name.clear();
161 current_function = false;
162 at_pc = false;
163 frame_line = false;
164 start_addr = LLDB_INVALID_ADDRESS;
165 end_addr = LLDB_INVALID_ADDRESS;
166 symbol_containing_addr = LLDB_INVALID_ADDRESS;
167 raw = false;
168 plugin_name.clear();
169
170 Target *target =
171 execution_context ? execution_context->GetTargetPtr() : nullptr;
172
173 // This is a hack till we get the ability to specify features based on
174 // architecture. For now GetDisassemblyFlavor is really only valid for x86
175 // (and for the llvm assembler plugin, but I'm papering over that since that
176 // is the only disassembler plugin we have...
177 if (target) {
178 if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
179 target->GetArchitecture().GetTriple().getArch() ==
180 llvm::Triple::x86_64) {
181 flavor_string.assign(target->GetDisassemblyFlavor());
182 } else
183 flavor_string.assign("default");
184
185 } else
186 flavor_string.assign("default");
187
188 arch.Clear();
189 some_location_specified = false;
190 force = false;
191 }
192
OptionParsingFinished(ExecutionContext * execution_context)193 Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
194 ExecutionContext *execution_context) {
195 if (!some_location_specified)
196 current_function = true;
197 return Status();
198 }
199
200 llvm::ArrayRef<OptionDefinition>
GetDefinitions()201 CommandObjectDisassemble::CommandOptions::GetDefinitions() {
202 return llvm::makeArrayRef(g_disassemble_options);
203 }
204
205 // CommandObjectDisassemble
206
CommandObjectDisassemble(CommandInterpreter & interpreter)207 CommandObjectDisassemble::CommandObjectDisassemble(
208 CommandInterpreter &interpreter)
209 : CommandObjectParsed(
210 interpreter, "disassemble",
211 "Disassemble specified instructions in the current target. "
212 "Defaults to the current function for the current thread and "
213 "stack frame.",
214 "disassemble [<cmd-options>]", eCommandRequiresTarget),
215 m_options() {}
216
217 CommandObjectDisassemble::~CommandObjectDisassemble() = default;
218
CheckRangeSize(const AddressRange & range,llvm::StringRef what)219 llvm::Error CommandObjectDisassemble::CheckRangeSize(const AddressRange &range,
220 llvm::StringRef what) {
221 if (m_options.num_instructions > 0 || m_options.force ||
222 range.GetByteSize() < GetDebugger().GetStopDisassemblyMaxSize())
223 return llvm::Error::success();
224 StreamString msg;
225 msg << "Not disassembling " << what << " because it is very large ";
226 range.Dump(&msg, &GetSelectedTarget(), Address::DumpStyleLoadAddress,
227 Address::DumpStyleFileAddress);
228 msg << ". To disassemble specify an instruction count limit, start/stop "
229 "addresses or use the --force option.";
230 return llvm::createStringError(llvm::inconvertibleErrorCode(),
231 msg.GetString());
232 }
233
234 llvm::Expected<std::vector<AddressRange>>
GetContainingAddressRanges()235 CommandObjectDisassemble::GetContainingAddressRanges() {
236 std::vector<AddressRange> ranges;
237 const auto &get_range = [&](Address addr) {
238 ModuleSP module_sp(addr.GetModule());
239 SymbolContext sc;
240 bool resolve_tail_call_address = true;
241 addr.GetModule()->ResolveSymbolContextForAddress(
242 addr, eSymbolContextEverything, sc, resolve_tail_call_address);
243 if (sc.function || sc.symbol) {
244 AddressRange range;
245 sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
246 false, range);
247 ranges.push_back(range);
248 }
249 };
250
251 Target &target = GetSelectedTarget();
252 if (!target.GetSectionLoadList().IsEmpty()) {
253 Address symbol_containing_address;
254 if (target.GetSectionLoadList().ResolveLoadAddress(
255 m_options.symbol_containing_addr, symbol_containing_address)) {
256 get_range(symbol_containing_address);
257 }
258 } else {
259 for (lldb::ModuleSP module_sp : target.GetImages().Modules()) {
260 Address file_address;
261 if (module_sp->ResolveFileAddress(m_options.symbol_containing_addr,
262 file_address)) {
263 get_range(file_address);
264 }
265 }
266 }
267
268 if (ranges.empty()) {
269 return llvm::createStringError(
270 llvm::inconvertibleErrorCode(),
271 "Could not find function bounds for address 0x%" PRIx64,
272 m_options.symbol_containing_addr);
273 }
274
275 if (llvm::Error err = CheckRangeSize(ranges[0], "the function"))
276 return std::move(err);
277 return ranges;
278 }
279
280 llvm::Expected<std::vector<AddressRange>>
GetCurrentFunctionRanges()281 CommandObjectDisassemble::GetCurrentFunctionRanges() {
282 StackFrame *frame = m_exe_ctx.GetFramePtr();
283 if (!frame) {
284 return llvm::createStringError(llvm::inconvertibleErrorCode(),
285 "Cannot disassemble around the current "
286 "function without a selected frame.\n");
287 }
288 SymbolContext sc(
289 frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
290 AddressRange range;
291 if (sc.function)
292 range = sc.function->GetAddressRange();
293 else if (sc.symbol && sc.symbol->ValueIsAddress()) {
294 range = {sc.symbol->GetAddress(), sc.symbol->GetByteSize()};
295 } else
296 range = {frame->GetFrameCodeAddress(), default_disasm_byte_size};
297
298 if (llvm::Error err = CheckRangeSize(range, "the current function"))
299 return std::move(err);
300 return std::vector<AddressRange>{range};
301 }
302
303 llvm::Expected<std::vector<AddressRange>>
GetCurrentLineRanges()304 CommandObjectDisassemble::GetCurrentLineRanges() {
305 StackFrame *frame = m_exe_ctx.GetFramePtr();
306 if (!frame) {
307 return llvm::createStringError(llvm::inconvertibleErrorCode(),
308 "Cannot disassemble around the current "
309 "line without a selected frame.\n");
310 }
311
312 LineEntry pc_line_entry(
313 frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
314 if (pc_line_entry.IsValid())
315 return std::vector<AddressRange>{pc_line_entry.range};
316
317 // No line entry, so just disassemble around the current pc
318 m_options.show_mixed = false;
319 return GetPCRanges();
320 }
321
322 llvm::Expected<std::vector<AddressRange>>
GetNameRanges(CommandReturnObject & result)323 CommandObjectDisassemble::GetNameRanges(CommandReturnObject &result) {
324 ConstString name(m_options.func_name.c_str());
325 const bool include_symbols = true;
326 const bool include_inlines = true;
327
328 // Find functions matching the given name.
329 SymbolContextList sc_list;
330 GetSelectedTarget().GetImages().FindFunctions(
331 name, eFunctionNameTypeAuto, include_symbols, include_inlines, sc_list);
332
333 std::vector<AddressRange> ranges;
334 llvm::Error range_errs = llvm::Error::success();
335 AddressRange range;
336 const uint32_t scope =
337 eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
338 const bool use_inline_block_range = true;
339 for (SymbolContext sc : sc_list.SymbolContexts()) {
340 for (uint32_t range_idx = 0;
341 sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
342 ++range_idx) {
343 if (llvm::Error err = CheckRangeSize(range, "a range"))
344 range_errs = joinErrors(std::move(range_errs), std::move(err));
345 else
346 ranges.push_back(range);
347 }
348 }
349 if (ranges.empty()) {
350 if (range_errs)
351 return std::move(range_errs);
352 return llvm::createStringError(llvm::inconvertibleErrorCode(),
353 "Unable to find symbol with name '%s'.\n",
354 name.GetCString());
355 }
356 if (range_errs)
357 result.AppendWarning(toString(std::move(range_errs)));
358 return ranges;
359 }
360
361 llvm::Expected<std::vector<AddressRange>>
GetPCRanges()362 CommandObjectDisassemble::GetPCRanges() {
363 StackFrame *frame = m_exe_ctx.GetFramePtr();
364 if (!frame) {
365 return llvm::createStringError(llvm::inconvertibleErrorCode(),
366 "Cannot disassemble around the current "
367 "PC without a selected frame.\n");
368 }
369
370 if (m_options.num_instructions == 0) {
371 // Disassembling at the PC always disassembles some number of
372 // instructions (not the whole function).
373 m_options.num_instructions = default_disasm_num_ins;
374 }
375 return std::vector<AddressRange>{{frame->GetFrameCodeAddress(), 0}};
376 }
377
378 llvm::Expected<std::vector<AddressRange>>
GetStartEndAddressRanges()379 CommandObjectDisassemble::GetStartEndAddressRanges() {
380 addr_t size = 0;
381 if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
382 if (m_options.end_addr <= m_options.start_addr) {
383 return llvm::createStringError(llvm::inconvertibleErrorCode(),
384 "End address before start address.");
385 }
386 size = m_options.end_addr - m_options.start_addr;
387 }
388 return std::vector<AddressRange>{{Address(m_options.start_addr), size}};
389 }
390
391 llvm::Expected<std::vector<AddressRange>>
GetRangesForSelectedMode(CommandReturnObject & result)392 CommandObjectDisassemble::GetRangesForSelectedMode(
393 CommandReturnObject &result) {
394 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
395 return CommandObjectDisassemble::GetContainingAddressRanges();
396 if (m_options.current_function)
397 return CommandObjectDisassemble::GetCurrentFunctionRanges();
398 if (m_options.frame_line)
399 return CommandObjectDisassemble::GetCurrentLineRanges();
400 if (!m_options.func_name.empty())
401 return CommandObjectDisassemble::GetNameRanges(result);
402 if (m_options.start_addr != LLDB_INVALID_ADDRESS)
403 return CommandObjectDisassemble::GetStartEndAddressRanges();
404 return CommandObjectDisassemble::GetPCRanges();
405 }
406
DoExecute(Args & command,CommandReturnObject & result)407 bool CommandObjectDisassemble::DoExecute(Args &command,
408 CommandReturnObject &result) {
409 Target *target = &GetSelectedTarget();
410
411 if (!m_options.arch.IsValid())
412 m_options.arch = target->GetArchitecture();
413
414 if (!m_options.arch.IsValid()) {
415 result.AppendError(
416 "use the --arch option or set the target architecture to disassemble");
417 return false;
418 }
419
420 const char *plugin_name = m_options.GetPluginName();
421 const char *flavor_string = m_options.GetFlavorString();
422
423 DisassemblerSP disassembler =
424 Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
425
426 if (!disassembler) {
427 if (plugin_name) {
428 result.AppendErrorWithFormat(
429 "Unable to find Disassembler plug-in named '%s' that supports the "
430 "'%s' architecture.\n",
431 plugin_name, m_options.arch.GetArchitectureName());
432 } else
433 result.AppendErrorWithFormat(
434 "Unable to find Disassembler plug-in for the '%s' architecture.\n",
435 m_options.arch.GetArchitectureName());
436 return false;
437 } else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec(
438 m_options.arch, flavor_string))
439 result.AppendWarningWithFormat(
440 "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
441
442 result.SetStatus(eReturnStatusSuccessFinishResult);
443
444 if (!command.empty()) {
445 result.AppendErrorWithFormat(
446 "\"disassemble\" arguments are specified as options.\n");
447 const int terminal_width =
448 GetCommandInterpreter().GetDebugger().GetTerminalWidth();
449 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
450 terminal_width);
451 return false;
452 }
453
454 if (m_options.show_mixed && m_options.num_lines_context == 0)
455 m_options.num_lines_context = 2;
456
457 // Always show the PC in the disassembly
458 uint32_t options = Disassembler::eOptionMarkPCAddress;
459
460 // Mark the source line for the current PC only if we are doing mixed source
461 // and assembly
462 if (m_options.show_mixed)
463 options |= Disassembler::eOptionMarkPCSourceLine;
464
465 if (m_options.show_bytes)
466 options |= Disassembler::eOptionShowBytes;
467
468 if (m_options.raw)
469 options |= Disassembler::eOptionRawOuput;
470
471 llvm::Expected<std::vector<AddressRange>> ranges =
472 GetRangesForSelectedMode(result);
473 if (!ranges) {
474 result.AppendError(toString(ranges.takeError()));
475 return result.Succeeded();
476 }
477
478 bool print_sc_header = ranges->size() > 1;
479 for (AddressRange cur_range : *ranges) {
480 Disassembler::Limit limit;
481 if (m_options.num_instructions == 0) {
482 limit = {Disassembler::Limit::Bytes, cur_range.GetByteSize()};
483 if (limit.value == 0)
484 limit.value = default_disasm_byte_size;
485 } else {
486 limit = {Disassembler::Limit::Instructions, m_options.num_instructions};
487 }
488 if (Disassembler::Disassemble(
489 GetDebugger(), m_options.arch, plugin_name, flavor_string,
490 m_exe_ctx, cur_range.GetBaseAddress(), limit, m_options.show_mixed,
491 m_options.show_mixed ? m_options.num_lines_context : 0, options,
492 result.GetOutputStream())) {
493 result.SetStatus(eReturnStatusSuccessFinishResult);
494 } else {
495 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
496 result.AppendErrorWithFormat(
497 "Failed to disassemble memory in function at 0x%8.8" PRIx64 ".\n",
498 m_options.symbol_containing_addr);
499 } else {
500 result.AppendErrorWithFormat(
501 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
502 cur_range.GetBaseAddress().GetLoadAddress(target));
503 }
504 }
505 if (print_sc_header)
506 result.GetOutputStream() << "\n";
507 }
508
509 return result.Succeeded();
510 }
511