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