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