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