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