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
CommandOptions(CommandInterpreter & interpreter)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
~CommandOptions()57 CommandObjectDisassemble::CommandOptions::~CommandOptions ()
58 {
59 }
60
61 Error
SetOptionValue(uint32_t option_idx,const char * option_arg)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
OptionParsingStarting()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
OptionParsingFinished()216 CommandObjectDisassemble::CommandOptions::OptionParsingFinished ()
217 {
218 if (!some_location_specified)
219 current_function = true;
220 return Error();
221
222 }
223
224 const OptionDefinition*
GetDefinitions()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
CommandObjectDisassemble(CommandInterpreter & interpreter)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
~CommandObjectDisassemble()273 CommandObjectDisassemble::~CommandObjectDisassemble()
274 {
275 }
276
277 bool
DoExecute(Args & command,CommandReturnObject & result)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 architecure 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