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