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     llvm_unreachable("Unimplemented option");
151   }
152 
153   return error;
154 }
155 
156 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
157     ExecutionContext *execution_context) {
158   show_mixed = false;
159   show_bytes = false;
160   num_lines_context = 0;
161   num_instructions = 0;
162   func_name.clear();
163   current_function = false;
164   at_pc = false;
165   frame_line = false;
166   start_addr = LLDB_INVALID_ADDRESS;
167   end_addr = LLDB_INVALID_ADDRESS;
168   symbol_containing_addr = LLDB_INVALID_ADDRESS;
169   raw = false;
170   plugin_name.clear();
171 
172   Target *target =
173       execution_context ? execution_context->GetTargetPtr() : nullptr;
174 
175   // This is a hack till we get the ability to specify features based on
176   // architecture.  For now GetDisassemblyFlavor is really only valid for x86
177   // (and for the llvm assembler plugin, but I'm papering over that since that
178   // is the only disassembler plugin we have...
179   if (target) {
180     if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
181         target->GetArchitecture().GetTriple().getArch() ==
182             llvm::Triple::x86_64) {
183       flavor_string.assign(target->GetDisassemblyFlavor());
184     } else
185       flavor_string.assign("default");
186 
187   } else
188     flavor_string.assign("default");
189 
190   arch.Clear();
191   some_location_specified = false;
192 }
193 
194 Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
195     ExecutionContext *execution_context) {
196   if (!some_location_specified)
197     current_function = true;
198   return Status();
199 }
200 
201 llvm::ArrayRef<OptionDefinition>
202 CommandObjectDisassemble::CommandOptions::GetDefinitions() {
203   return llvm::makeArrayRef(g_disassemble_options);
204 }
205 
206 // CommandObjectDisassemble
207 
208 CommandObjectDisassemble::CommandObjectDisassemble(
209     CommandInterpreter &interpreter)
210     : CommandObjectParsed(
211           interpreter, "disassemble",
212           "Disassemble specified instructions in the current target.  "
213           "Defaults to the current function for the current thread and "
214           "stack frame.",
215           "disassemble [<cmd-options>]"),
216       m_options() {}
217 
218 CommandObjectDisassemble::~CommandObjectDisassemble() = default;
219 
220 bool CommandObjectDisassemble::DoExecute(Args &command,
221                                          CommandReturnObject &result) {
222   Target *target = GetDebugger().GetSelectedTarget().get();
223   if (target == nullptr) {
224     result.AppendError("invalid target, create a debug target using the "
225                        "'target create' command");
226     result.SetStatus(eReturnStatusFailed);
227     return false;
228   }
229   if (!m_options.arch.IsValid())
230     m_options.arch = target->GetArchitecture();
231 
232   if (!m_options.arch.IsValid()) {
233     result.AppendError(
234         "use the --arch option or set the target architecture to disassemble");
235     result.SetStatus(eReturnStatusFailed);
236     return false;
237   }
238 
239   const char *plugin_name = m_options.GetPluginName();
240   const char *flavor_string = m_options.GetFlavorString();
241 
242   DisassemblerSP disassembler =
243       Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
244 
245   if (!disassembler) {
246     if (plugin_name) {
247       result.AppendErrorWithFormat(
248           "Unable to find Disassembler plug-in named '%s' that supports the "
249           "'%s' architecture.\n",
250           plugin_name, m_options.arch.GetArchitectureName());
251     } else
252       result.AppendErrorWithFormat(
253           "Unable to find Disassembler plug-in for the '%s' architecture.\n",
254           m_options.arch.GetArchitectureName());
255     result.SetStatus(eReturnStatusFailed);
256     return false;
257   } else if (flavor_string != nullptr &&
258              !disassembler->FlavorValidForArchSpec(m_options.arch,
259                                                    flavor_string))
260     result.AppendWarningWithFormat(
261         "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
262 
263   result.SetStatus(eReturnStatusSuccessFinishResult);
264 
265   if (!command.empty()) {
266     result.AppendErrorWithFormat(
267         "\"disassemble\" arguments are specified as options.\n");
268     const int terminal_width =
269         GetCommandInterpreter().GetDebugger().GetTerminalWidth();
270     GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
271                                       terminal_width);
272     result.SetStatus(eReturnStatusFailed);
273     return false;
274   }
275 
276   if (m_options.show_mixed && m_options.num_lines_context == 0)
277     m_options.num_lines_context = 2;
278 
279   // Always show the PC in the disassembly
280   uint32_t options = Disassembler::eOptionMarkPCAddress;
281 
282   // Mark the source line for the current PC only if we are doing mixed source
283   // and assembly
284   if (m_options.show_mixed)
285     options |= Disassembler::eOptionMarkPCSourceLine;
286 
287   if (m_options.show_bytes)
288     options |= Disassembler::eOptionShowBytes;
289 
290   if (m_options.raw)
291     options |= Disassembler::eOptionRawOuput;
292 
293   if (!m_options.func_name.empty()) {
294     ConstString name(m_options.func_name.c_str());
295 
296     if (Disassembler::Disassemble(
297             GetDebugger(), m_options.arch, plugin_name, flavor_string,
298             m_exe_ctx, name,
299             nullptr, // Module *
300             m_options.num_instructions, m_options.show_mixed,
301             m_options.show_mixed ? m_options.num_lines_context : 0, options,
302             result.GetOutputStream())) {
303       result.SetStatus(eReturnStatusSuccessFinishResult);
304     } else {
305       result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
306                                    name.GetCString());
307       result.SetStatus(eReturnStatusFailed);
308     }
309   } else {
310     std::vector<AddressRange> ranges;
311     AddressRange range;
312     StackFrame *frame = m_exe_ctx.GetFramePtr();
313     if (m_options.frame_line) {
314       if (frame == nullptr) {
315         result.AppendError("Cannot disassemble around the current line without "
316                            "a selected frame.\n");
317         result.SetStatus(eReturnStatusFailed);
318         return false;
319       }
320       LineEntry pc_line_entry(
321           frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
322       if (pc_line_entry.IsValid()) {
323         range = pc_line_entry.range;
324       } else {
325         m_options.at_pc =
326             true; // No line entry, so just disassemble around the current pc
327         m_options.show_mixed = false;
328       }
329     } else if (m_options.current_function) {
330       if (frame == nullptr) {
331         result.AppendError("Cannot disassemble around the current function "
332                            "without a selected frame.\n");
333         result.SetStatus(eReturnStatusFailed);
334         return false;
335       }
336       Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
337       if (symbol) {
338         range.GetBaseAddress() = symbol->GetAddress();
339         range.SetByteSize(symbol->GetByteSize());
340       }
341     }
342 
343     // Did the "m_options.frame_line" find a valid range already? If so skip
344     // the rest...
345     if (range.GetByteSize() == 0) {
346       if (m_options.at_pc) {
347         if (frame == nullptr) {
348           result.AppendError("Cannot disassemble around the current PC without "
349                              "a selected frame.\n");
350           result.SetStatus(eReturnStatusFailed);
351           return false;
352         }
353         range.GetBaseAddress() = frame->GetFrameCodeAddress();
354         if (m_options.num_instructions == 0) {
355           // Disassembling at the PC always disassembles some number of
356           // instructions (not the whole function).
357           m_options.num_instructions = DEFAULT_DISASM_NUM_INS;
358         }
359         ranges.push_back(range);
360       } else {
361         range.GetBaseAddress().SetOffset(m_options.start_addr);
362         if (range.GetBaseAddress().IsValid()) {
363           if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
364             if (m_options.end_addr <= m_options.start_addr) {
365               result.AppendErrorWithFormat(
366                   "End address before start address.\n");
367               result.SetStatus(eReturnStatusFailed);
368               return false;
369             }
370             range.SetByteSize(m_options.end_addr - m_options.start_addr);
371           }
372           ranges.push_back(range);
373         } else {
374           if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS &&
375               target) {
376             if (!target->GetSectionLoadList().IsEmpty()) {
377               bool failed = false;
378               Address symbol_containing_address;
379               if (target->GetSectionLoadList().ResolveLoadAddress(
380                       m_options.symbol_containing_addr,
381                       symbol_containing_address)) {
382                 ModuleSP module_sp(symbol_containing_address.GetModule());
383                 SymbolContext sc;
384                 bool resolve_tail_call_address = true; // PC can be one past the
385                                                        // address range of the
386                                                        // function.
387                 module_sp->ResolveSymbolContextForAddress(
388                     symbol_containing_address, eSymbolContextEverything, sc,
389                     resolve_tail_call_address);
390                 if (sc.function || sc.symbol) {
391                   sc.GetAddressRange(eSymbolContextFunction |
392                                          eSymbolContextSymbol,
393                                      0, false, range);
394                 } else {
395                   failed = true;
396                 }
397               } else {
398                 failed = true;
399               }
400               if (failed) {
401                 result.AppendErrorWithFormat(
402                     "Could not find function bounds for address 0x%" PRIx64
403                     "\n",
404                     m_options.symbol_containing_addr);
405                 result.SetStatus(eReturnStatusFailed);
406                 return false;
407               }
408               ranges.push_back(range);
409             } else {
410               for (lldb::ModuleSP module_sp : target->GetImages().Modules()) {
411                 lldb::addr_t file_addr = m_options.symbol_containing_addr;
412                 Address file_address;
413                 if (module_sp->ResolveFileAddress(file_addr, file_address)) {
414                   SymbolContext sc;
415                   bool resolve_tail_call_address = true; // PC can be one past
416                                                          // the address range of
417                                                          // the function.
418                   module_sp->ResolveSymbolContextForAddress(
419                       file_address, eSymbolContextEverything, sc,
420                       resolve_tail_call_address);
421                   if (sc.function || sc.symbol) {
422                     sc.GetAddressRange(eSymbolContextFunction |
423                                            eSymbolContextSymbol,
424                                        0, false, range);
425                     ranges.push_back(range);
426                   }
427                 }
428               }
429             }
430           }
431         }
432       }
433     } else
434       ranges.push_back(range);
435 
436     if (m_options.num_instructions != 0) {
437       if (ranges.empty()) {
438         // The default action is to disassemble the current frame function.
439         if (frame) {
440           SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
441                                                    eSymbolContextSymbol));
442           if (sc.function)
443             range.GetBaseAddress() =
444                 sc.function->GetAddressRange().GetBaseAddress();
445           else if (sc.symbol && sc.symbol->ValueIsAddress())
446             range.GetBaseAddress() = sc.symbol->GetAddress();
447           else
448             range.GetBaseAddress() = frame->GetFrameCodeAddress();
449         }
450 
451         if (!range.GetBaseAddress().IsValid()) {
452           result.AppendError("invalid frame");
453           result.SetStatus(eReturnStatusFailed);
454           return false;
455         }
456       }
457 
458       bool print_sc_header = ranges.size() > 1;
459       for (AddressRange cur_range : ranges) {
460         if (Disassembler::Disassemble(
461                 GetDebugger(), m_options.arch, plugin_name, flavor_string,
462                 m_exe_ctx, cur_range.GetBaseAddress(),
463                 m_options.num_instructions, m_options.show_mixed,
464                 m_options.show_mixed ? m_options.num_lines_context : 0, options,
465                 result.GetOutputStream())) {
466           result.SetStatus(eReturnStatusSuccessFinishResult);
467         } else {
468           if (m_options.start_addr != LLDB_INVALID_ADDRESS)
469             result.AppendErrorWithFormat(
470                 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
471                 m_options.start_addr);
472           else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
473             result.AppendErrorWithFormat(
474                 "Failed to disassemble memory in function at 0x%8.8" PRIx64
475                 ".\n",
476                 m_options.symbol_containing_addr);
477           result.SetStatus(eReturnStatusFailed);
478         }
479       }
480       if (print_sc_header)
481         result.AppendMessage("\n");
482     } else {
483       if (ranges.empty()) {
484         // The default action is to disassemble the current frame function.
485         if (frame) {
486           SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
487                                                    eSymbolContextSymbol));
488           if (sc.function)
489             range = sc.function->GetAddressRange();
490           else if (sc.symbol && sc.symbol->ValueIsAddress()) {
491             range.GetBaseAddress() = sc.symbol->GetAddress();
492             range.SetByteSize(sc.symbol->GetByteSize());
493           } else
494             range.GetBaseAddress() = frame->GetFrameCodeAddress();
495         } else {
496           result.AppendError("invalid frame");
497           result.SetStatus(eReturnStatusFailed);
498           return false;
499         }
500         ranges.push_back(range);
501       }
502 
503       bool print_sc_header = ranges.size() > 1;
504       for (AddressRange cur_range : ranges) {
505         if (cur_range.GetByteSize() == 0)
506           cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
507 
508         if (Disassembler::Disassemble(
509                 GetDebugger(), m_options.arch, plugin_name, flavor_string,
510                 m_exe_ctx, cur_range, m_options.num_instructions,
511                 m_options.show_mixed,
512                 m_options.show_mixed ? m_options.num_lines_context : 0, options,
513                 result.GetOutputStream())) {
514           result.SetStatus(eReturnStatusSuccessFinishResult);
515         } else {
516           result.AppendErrorWithFormat(
517               "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
518               m_options.start_addr);
519           result.SetStatus(eReturnStatusFailed);
520         }
521         if (print_sc_header)
522           result.AppendMessage("\n");
523       }
524     }
525   }
526 
527   return result.Succeeded();
528 }
529