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/Args.h"
18 #include "lldb/Interpreter/CommandCompletions.h"
19 #include "lldb/Interpreter/CommandInterpreter.h"
20 #include "lldb/Interpreter/CommandReturnObject.h"
21 #include "lldb/Core/Disassembler.h"
22 #include "lldb/Core/Options.h"
23 #include "lldb/Core/SourceManager.h"
24 #include "lldb/Target/StackFrame.h"
25 #include "lldb/Symbol/Symbol.h"
26 #include "lldb/Target/Process.h"
27 #include "lldb/Target/Target.h"
28 
29 #define DEFAULT_DISASM_BYTE_SIZE 32
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 CommandObjectDisassemble::CommandOptions::CommandOptions () :
35     Options(),
36     m_func_name(),
37     m_load_addr()
38 {
39     ResetOptionValues();
40 }
41 
42 CommandObjectDisassemble::CommandOptions::~CommandOptions ()
43 {
44 }
45 
46 Error
47 CommandObjectDisassemble::CommandOptions::SetOptionValue (int option_idx, const char *option_arg)
48 {
49     Error error;
50 
51     char short_option = (char) m_getopt_table[option_idx].val;
52 
53     switch (short_option)
54     {
55     case 'm':
56         show_mixed = true;
57         break;
58 
59     case 'c':
60         num_lines_context = Args::StringToUInt32(option_arg, 0, 0);
61         break;
62 
63     case 'b':
64         show_bytes = true;
65         break;
66 
67     case 'a':
68         m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 0);
69         if (m_load_addr == LLDB_INVALID_ADDRESS)
70             m_load_addr = Args::StringToUInt64(optarg, LLDB_INVALID_ADDRESS, 16);
71 
72         if (m_load_addr == LLDB_INVALID_ADDRESS)
73             error.SetErrorStringWithFormat ("Invalid address string '%s'.\n", optarg);
74         break;
75 
76     case 'n':
77         m_func_name = option_arg;
78         break;
79 
80     case 'r':
81         raw = true;
82         break;
83 
84     default:
85         error.SetErrorStringWithFormat("Unrecognized short option '%c'.\n", short_option);
86         break;
87     }
88 
89     return error;
90 }
91 
92 void
93 CommandObjectDisassemble::CommandOptions::ResetOptionValues ()
94 {
95     Options::ResetOptionValues();
96     show_mixed = false;
97     show_bytes = false;
98     num_lines_context = 0;
99     m_func_name.clear();
100     m_load_addr = LLDB_INVALID_ADDRESS;
101 }
102 
103 const lldb::OptionDefinition*
104 CommandObjectDisassemble::CommandOptions::GetDefinitions ()
105 {
106     return g_option_table;
107 }
108 
109 lldb::OptionDefinition
110 CommandObjectDisassemble::CommandOptions::g_option_table[] =
111 {
112 { 0, false, "bytes",    'b', no_argument,       NULL, 0, NULL,             "Show opcode bytes when disassembling."},
113 { 0, false, "context",  'c', required_argument, NULL, 0, "<num-lines>",    "Number of context lines of source to show."},
114 { 0, false, "mixed",    'm', no_argument,       NULL, 0, NULL,             "Enable mixed source and assembly display."},
115 { 0, false, "raw",      'r', no_argument,       NULL, 0, NULL,             "Print raw disassembly with no symbol information."},
116 
117 { 1, false, "address",  'a', required_argument, NULL, 0, "<address>",      "Address to start disassembling."},
118 { 1, false, "bytes",    'b', no_argument,       NULL, 0, NULL,             "Show opcode bytes when disassembling."},
119 { 1, false, "context",  'c', required_argument, NULL, 0, "<num-lines>",    "Number of context lines of source to show."},
120 { 1, false, "mixed",    'm', no_argument,       NULL, 0, NULL,             "Enable mixed source and assembly display."},
121 { 1, false, "raw",      'r', no_argument,       NULL, 0, NULL,             "Print raw disassembly with no symbol information."},
122 
123 { 2, false, "name",     'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, "<function-name>",             "Disassemble entire contents of the given function name."},
124 { 2, false, "bytes",    'b', no_argument,       NULL, 0, NULL,             "Show opcode bytes when disassembling."},
125 { 2, false, "context",  'c', required_argument, NULL, 0, "<num-lines>",    "Number of context lines of source to show."},
126 { 2, false, "mixed",    'm', no_argument,       NULL, 0, NULL,             "Enable mixed source and assembly display."},
127 { 2, false, "raw",      'r', no_argument,       NULL, 0, NULL,             "Print raw disassembly with no symbol information."},
128 
129 { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
130 };
131 
132 
133 
134 //-------------------------------------------------------------------------
135 // CommandObjectDisassemble
136 //-------------------------------------------------------------------------
137 
138 CommandObjectDisassemble::CommandObjectDisassemble () :
139     CommandObject ("disassemble",
140                      "Disassemble bytes in the current function or anywhere in the inferior program.",
141                      "disassemble [[<start-addr> [<end-addr>]] | <function-name>] [<cmd-options>]")
142 {
143 }
144 
145 CommandObjectDisassemble::~CommandObjectDisassemble()
146 {
147 }
148 
149 void
150 CommandObjectDisassemble::Disassemble
151 (
152     CommandContext *context,
153     CommandInterpreter *interpreter,
154     CommandReturnObject &result,
155     Disassembler *disassembler,
156     const SymbolContextList &sc_list
157 )
158 {
159     const size_t count = sc_list.GetSize();
160     SymbolContext sc;
161     AddressRange range;
162     for (size_t i=0; i<count; ++i)
163     {
164         if (sc_list.GetContextAtIndex(i, sc) == false)
165             break;
166         if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, range))
167         {
168             lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(context->GetExecutionContext().process);
169             if (addr != LLDB_INVALID_ADDRESS)
170             {
171                 lldb::addr_t end_addr = addr + range.GetByteSize();
172                 Disassemble (context, interpreter, result, disassembler, addr, end_addr);
173             }
174         }
175     }
176 }
177 
178 void
179 CommandObjectDisassemble::Disassemble
180 (
181     CommandContext *context,
182     CommandInterpreter *interpreter,
183     CommandReturnObject &result,
184     Disassembler *disassembler,
185     lldb::addr_t addr,
186     lldb::addr_t end_addr
187 )
188 {
189     if (addr == LLDB_INVALID_ADDRESS)
190         return;
191 
192     if (end_addr == LLDB_INVALID_ADDRESS || addr >= end_addr)
193         end_addr = addr + DEFAULT_DISASM_BYTE_SIZE;
194 
195     ExecutionContext exe_ctx (context->GetExecutionContext());
196     DataExtractor data;
197     size_t bytes_disassembled = disassembler->ParseInstructions (&exe_ctx, eAddressTypeLoad, addr, end_addr - addr, data);
198     if (bytes_disassembled == 0)
199     {
200         // Nothing got disassembled...
201     }
202     else
203     {
204         // We got some things disassembled...
205         size_t num_instructions = disassembler->GetInstructionList().GetSize();
206         uint32_t offset = 0;
207         Stream &output_stream = result.GetOutputStream();
208         SymbolContext sc;
209         SymbolContext prev_sc;
210         AddressRange sc_range;
211         if (m_options.show_mixed)
212             output_stream.IndentMore ();
213 
214         for (size_t i=0; i<num_instructions; ++i)
215         {
216             Disassembler::Instruction *inst = disassembler->GetInstructionList().GetInstructionAtIndex (i);
217             if (inst)
218             {
219                 lldb::addr_t curr_addr = addr + offset;
220                 if (m_options.show_mixed)
221                 {
222                     Process *process = context->GetExecutionContext().process;
223                     if (!sc_range.ContainsLoadAddress (curr_addr, process))
224                     {
225                         prev_sc = sc;
226                         Address curr_so_addr;
227                         if (process && process->ResolveLoadAddress (curr_addr, curr_so_addr))
228                         {
229                             if (curr_so_addr.GetSection())
230                             {
231                                 Module *module = curr_so_addr.GetSection()->GetModule();
232                                 uint32_t resolved_mask = module->ResolveSymbolContextForAddress(curr_so_addr, eSymbolContextEverything, sc);
233                                 if (resolved_mask)
234                                 {
235                                     sc.GetAddressRange (eSymbolContextEverything, sc_range);
236                                     if (sc != prev_sc)
237                                     {
238                                         if (offset != 0)
239                                             output_stream.EOL();
240 
241                                         sc.DumpStopContext(&output_stream, process, curr_so_addr);
242                                         output_stream.EOL();
243                                         if (sc.comp_unit && sc.line_entry.IsValid())
244                                         {
245                                             interpreter->GetSourceManager().DisplaySourceLinesWithLineNumbers (
246                                                     sc.line_entry.file,
247                                                     sc.line_entry.line,
248                                                     m_options.num_lines_context,
249                                                     m_options.num_lines_context,
250                                                     m_options.num_lines_context ? "->" : "",
251                                                     &output_stream);
252                                         }
253                                     }
254                                 }
255                             }
256                         }
257                     }
258                 }
259                 if (m_options.show_mixed)
260                     output_stream.IndentMore ();
261                 output_stream.Indent();
262                 size_t inst_byte_size = inst->GetByteSize();
263                 inst->Dump(&output_stream, curr_addr, m_options.show_bytes ? &data : NULL, offset, exe_ctx, m_options.raw);
264                 output_stream.EOL();
265                 offset += inst_byte_size;
266                 if (m_options.show_mixed)
267                     output_stream.IndentLess ();
268             }
269             else
270             {
271                 break;
272             }
273         }
274         if (m_options.show_mixed)
275             output_stream.IndentLess ();
276 
277     }
278 }
279 
280 bool
281 CommandObjectDisassemble::Execute
282 (
283     Args& command,
284     CommandContext *context,
285     CommandInterpreter *interpreter,
286     CommandReturnObject &result
287 )
288 {
289     Target *target = context->GetTarget();
290     if (target == NULL)
291     {
292         result.AppendError ("invalid target, set executable file using 'file' command");
293         result.SetStatus (eReturnStatusFailed);
294         return false;
295     }
296 
297     ArchSpec arch(target->GetArchitecture());
298     if (!arch.IsValid())
299     {
300         result.AppendError ("target needs valid architecure in order to be able to disassemble");
301         result.SetStatus (eReturnStatusFailed);
302         return false;
303     }
304 
305     Disassembler *disassembler = Disassembler::FindPlugin(arch);
306 
307     if (disassembler == NULL)
308     {
309         result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for %s architecture.\n", arch.AsCString());
310         result.SetStatus (eReturnStatusFailed);
311         return false;
312     }
313 
314     result.SetStatus (eReturnStatusSuccessFinishResult);
315 
316     lldb::addr_t addr = LLDB_INVALID_ADDRESS;
317     lldb::addr_t end_addr = LLDB_INVALID_ADDRESS;
318     ConstString name;
319     const size_t argc = command.GetArgumentCount();
320     if (argc == 0 && m_options.m_load_addr != LLDB_INVALID_ADDRESS)
321     {
322         addr = m_options.m_load_addr;
323         end_addr = addr + DEFAULT_DISASM_BYTE_SIZE;
324     } else if (argc == 0 && !m_options.m_func_name.empty())
325     {
326         ConstString tmpname(m_options.m_func_name.c_str());
327         name = tmpname;
328     } else if (argc == 0)
329     {
330         ExecutionContext exe_ctx(context->GetExecutionContext());
331         if (exe_ctx.frame)
332         {
333             SymbolContext sc(exe_ctx.frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
334             if (sc.function)
335             {
336                 addr = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(exe_ctx.process);
337                 if (addr != LLDB_INVALID_ADDRESS)
338                     end_addr = addr + sc.function->GetAddressRange().GetByteSize();
339             }
340             else if (sc.symbol && sc.symbol->GetAddressRangePtr())
341             {
342                 addr = sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetLoadAddress(exe_ctx.process);
343                 if (addr != LLDB_INVALID_ADDRESS)
344                 {
345                     end_addr = addr + sc.symbol->GetAddressRangePtr()->GetByteSize();
346                     if (addr == end_addr)
347                         end_addr += DEFAULT_DISASM_BYTE_SIZE;
348                 }
349             }
350             else
351             {
352                 addr = exe_ctx.frame->GetPC().GetLoadAddress(exe_ctx.process);
353                 if (addr != LLDB_INVALID_ADDRESS)
354                     end_addr = addr + DEFAULT_DISASM_BYTE_SIZE;
355             }
356         }
357         else
358         {
359             result.AppendError ("invalid frame");
360             result.SetStatus (eReturnStatusFailed);
361             return false;
362         }
363     }
364     else if (argc == 1)
365     {
366         const char *arg = command.GetArgumentAtIndex(0);
367         addr = Args::StringToAddress (arg);
368         if (addr == LLDB_INVALID_ADDRESS)
369         {
370             // Lookup function or symbol name?
371             ConstString tmpname(arg);
372             name = tmpname;
373         }
374         else
375         {
376             end_addr = addr + DEFAULT_DISASM_BYTE_SIZE;
377         }
378     }
379     else if (argc >= 1 && argc <= 2)
380     {
381         addr = Args::StringToAddress (command.GetArgumentAtIndex(0));
382         if (addr == LLDB_INVALID_ADDRESS)
383         {
384             result.AppendErrorWithFormat ("Unable to parse address '%s'.\n", command.GetArgumentAtIndex(0));
385             result.SetStatus (eReturnStatusFailed);
386             return false;
387         }
388         end_addr = Args::StringToAddress (command.GetArgumentAtIndex(1), addr);
389         if (end_addr == LLDB_INVALID_ADDRESS)
390         {
391             result.AppendErrorWithFormat ("Unable to parse address '%s'.\n", command.GetArgumentAtIndex(1));
392             result.SetStatus (eReturnStatusFailed);
393             return false;
394         }
395     }
396 
397     if (!name.IsEmpty())
398     {
399         SymbolContextList sc_list;
400 
401         if (target->GetImages().FindFunctions(name, sc_list))
402         {
403             Disassemble (context, interpreter, result, disassembler, sc_list);
404         }
405         else if (target->GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeCode, sc_list))
406         {
407             Disassemble (context, interpreter, result, disassembler, sc_list);
408         }
409         else
410         {
411             result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString());
412             result.SetStatus (eReturnStatusFailed);
413             return false;
414         }
415     }
416 
417     if (addr < end_addr)
418     {
419         Disassemble (context, interpreter, result, disassembler, addr, end_addr);
420     }
421 
422     if (addr == LLDB_INVALID_ADDRESS && name.IsEmpty())
423     {
424         result.AppendError ("No recognizable address of function name provided");
425         result.SetStatus (eReturnStatusFailed);
426         return false;
427     }
428     {
429         return result.Succeeded();
430     }
431 }
432