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