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