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