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