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