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