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