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