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