[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / source / Commands / CommandObjectDisassemble.cpp
blob63679e996e7071580c7fefcf520319fdc762e7bf
1 //===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectDisassemble.h"
10 #include "lldb/Core/AddressRange.h"
11 #include "lldb/Core/Disassembler.h"
12 #include "lldb/Core/Module.h"
13 #include "lldb/Host/OptionParser.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Interpreter/CommandReturnObject.h"
16 #include "lldb/Interpreter/OptionArgParser.h"
17 #include "lldb/Interpreter/Options.h"
18 #include "lldb/Symbol/Function.h"
19 #include "lldb/Symbol/Symbol.h"
20 #include "lldb/Target/SectionLoadList.h"
21 #include "lldb/Target/StackFrame.h"
22 #include "lldb/Target/Target.h"
24 #define DEFAULT_DISASM_BYTE_SIZE 32
25 #define DEFAULT_DISASM_NUM_INS 4
27 using namespace lldb;
28 using namespace lldb_private;
30 #define LLDB_OPTIONS_disassemble
31 #include "CommandOptions.inc"
33 CommandObjectDisassemble::CommandOptions::CommandOptions()
34 : Options(), num_lines_context(0), num_instructions(0), func_name(),
35 current_function(false), start_addr(), end_addr(), at_pc(false),
36 frame_line(false), plugin_name(), flavor_string(), arch(),
37 some_location_specified(false), symbol_containing_addr() {
38 OptionParsingStarting(nullptr);
41 CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
43 Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
44 uint32_t option_idx, llvm::StringRef option_arg,
45 ExecutionContext *execution_context) {
46 Status error;
48 const int short_option = m_getopt_table[option_idx].val;
50 switch (short_option) {
51 case 'm':
52 show_mixed = true;
53 break;
55 case 'C':
56 if (option_arg.getAsInteger(0, num_lines_context))
57 error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
58 option_arg.str().c_str());
59 break;
61 case 'c':
62 if (option_arg.getAsInteger(0, num_instructions))
63 error.SetErrorStringWithFormat(
64 "invalid num of instructions string: \"%s\"",
65 option_arg.str().c_str());
66 break;
68 case 'b':
69 show_bytes = true;
70 break;
72 case 's': {
73 start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
74 LLDB_INVALID_ADDRESS, &error);
75 if (start_addr != LLDB_INVALID_ADDRESS)
76 some_location_specified = true;
77 } break;
78 case 'e': {
79 end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
80 LLDB_INVALID_ADDRESS, &error);
81 if (end_addr != LLDB_INVALID_ADDRESS)
82 some_location_specified = true;
83 } break;
85 case 'n':
86 func_name.assign(option_arg);
87 some_location_specified = true;
88 break;
90 case 'p':
91 at_pc = true;
92 some_location_specified = true;
93 break;
95 case 'l':
96 frame_line = true;
97 // Disassemble the current source line kind of implies showing mixed source
98 // code context.
99 show_mixed = true;
100 some_location_specified = true;
101 break;
103 case 'P':
104 plugin_name.assign(option_arg);
105 break;
107 case 'F': {
108 TargetSP target_sp =
109 execution_context ? execution_context->GetTargetSP() : TargetSP();
110 if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
111 llvm::Triple::x86 ||
112 target_sp->GetArchitecture().GetTriple().getArch() ==
113 llvm::Triple::x86_64)) {
114 flavor_string.assign(option_arg);
115 } else
116 error.SetErrorStringWithFormat("Disassembler flavors are currently only "
117 "supported for x86 and x86_64 targets.");
118 break;
121 case 'r':
122 raw = true;
123 break;
125 case 'f':
126 current_function = true;
127 some_location_specified = true;
128 break;
130 case 'A':
131 if (execution_context) {
132 const auto &target_sp = execution_context->GetTargetSP();
133 auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
134 arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
136 break;
138 case 'a': {
139 symbol_containing_addr = OptionArgParser::ToAddress(
140 execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
141 if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
142 some_location_specified = true;
144 } break;
146 default:
147 llvm_unreachable("Unimplemented option");
150 return error;
153 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
154 ExecutionContext *execution_context) {
155 show_mixed = false;
156 show_bytes = false;
157 num_lines_context = 0;
158 num_instructions = 0;
159 func_name.clear();
160 current_function = false;
161 at_pc = false;
162 frame_line = false;
163 start_addr = LLDB_INVALID_ADDRESS;
164 end_addr = LLDB_INVALID_ADDRESS;
165 symbol_containing_addr = LLDB_INVALID_ADDRESS;
166 raw = false;
167 plugin_name.clear();
169 Target *target =
170 execution_context ? execution_context->GetTargetPtr() : nullptr;
172 // This is a hack till we get the ability to specify features based on
173 // architecture. For now GetDisassemblyFlavor is really only valid for x86
174 // (and for the llvm assembler plugin, but I'm papering over that since that
175 // is the only disassembler plugin we have...
176 if (target) {
177 if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
178 target->GetArchitecture().GetTriple().getArch() ==
179 llvm::Triple::x86_64) {
180 flavor_string.assign(target->GetDisassemblyFlavor());
181 } else
182 flavor_string.assign("default");
184 } else
185 flavor_string.assign("default");
187 arch.Clear();
188 some_location_specified = false;
191 Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
192 ExecutionContext *execution_context) {
193 if (!some_location_specified)
194 current_function = true;
195 return Status();
198 llvm::ArrayRef<OptionDefinition>
199 CommandObjectDisassemble::CommandOptions::GetDefinitions() {
200 return llvm::makeArrayRef(g_disassemble_options);
203 // CommandObjectDisassemble
205 CommandObjectDisassemble::CommandObjectDisassemble(
206 CommandInterpreter &interpreter)
207 : CommandObjectParsed(
208 interpreter, "disassemble",
209 "Disassemble specified instructions in the current target. "
210 "Defaults to the current function for the current thread and "
211 "stack frame.",
212 "disassemble [<cmd-options>]", eCommandRequiresTarget),
213 m_options() {}
215 CommandObjectDisassemble::~CommandObjectDisassemble() = default;
217 bool CommandObjectDisassemble::DoExecute(Args &command,
218 CommandReturnObject &result) {
219 Target *target = &GetSelectedTarget();
221 if (!m_options.arch.IsValid())
222 m_options.arch = target->GetArchitecture();
224 if (!m_options.arch.IsValid()) {
225 result.AppendError(
226 "use the --arch option or set the target architecture to disassemble");
227 result.SetStatus(eReturnStatusFailed);
228 return false;
231 const char *plugin_name = m_options.GetPluginName();
232 const char *flavor_string = m_options.GetFlavorString();
234 DisassemblerSP disassembler =
235 Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
237 if (!disassembler) {
238 if (plugin_name) {
239 result.AppendErrorWithFormat(
240 "Unable to find Disassembler plug-in named '%s' that supports the "
241 "'%s' architecture.\n",
242 plugin_name, m_options.arch.GetArchitectureName());
243 } else
244 result.AppendErrorWithFormat(
245 "Unable to find Disassembler plug-in for the '%s' architecture.\n",
246 m_options.arch.GetArchitectureName());
247 result.SetStatus(eReturnStatusFailed);
248 return false;
249 } else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec(
250 m_options.arch, flavor_string))
251 result.AppendWarningWithFormat(
252 "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
254 result.SetStatus(eReturnStatusSuccessFinishResult);
256 if (!command.empty()) {
257 result.AppendErrorWithFormat(
258 "\"disassemble\" arguments are specified as options.\n");
259 const int terminal_width =
260 GetCommandInterpreter().GetDebugger().GetTerminalWidth();
261 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
262 terminal_width);
263 result.SetStatus(eReturnStatusFailed);
264 return false;
267 if (m_options.show_mixed && m_options.num_lines_context == 0)
268 m_options.num_lines_context = 2;
270 // Always show the PC in the disassembly
271 uint32_t options = Disassembler::eOptionMarkPCAddress;
273 // Mark the source line for the current PC only if we are doing mixed source
274 // and assembly
275 if (m_options.show_mixed)
276 options |= Disassembler::eOptionMarkPCSourceLine;
278 if (m_options.show_bytes)
279 options |= Disassembler::eOptionShowBytes;
281 if (m_options.raw)
282 options |= Disassembler::eOptionRawOuput;
284 if (!m_options.func_name.empty()) {
285 ConstString name(m_options.func_name.c_str());
287 if (Disassembler::Disassemble(
288 GetDebugger(), m_options.arch, plugin_name, flavor_string,
289 m_exe_ctx, name,
290 nullptr, // Module *
291 m_options.num_instructions, m_options.show_mixed,
292 m_options.show_mixed ? m_options.num_lines_context : 0, options,
293 result.GetOutputStream())) {
294 result.SetStatus(eReturnStatusSuccessFinishResult);
295 } else {
296 result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
297 name.GetCString());
298 result.SetStatus(eReturnStatusFailed);
300 } else {
301 std::vector<AddressRange> ranges;
302 AddressRange range;
303 StackFrame *frame = m_exe_ctx.GetFramePtr();
304 if (m_options.frame_line) {
305 if (frame == nullptr) {
306 result.AppendError("Cannot disassemble around the current line without "
307 "a selected frame.\n");
308 result.SetStatus(eReturnStatusFailed);
309 return false;
311 LineEntry pc_line_entry(
312 frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
313 if (pc_line_entry.IsValid()) {
314 range = pc_line_entry.range;
315 } else {
316 m_options.at_pc =
317 true; // No line entry, so just disassemble around the current pc
318 m_options.show_mixed = false;
320 } else if (m_options.current_function) {
321 if (frame == nullptr) {
322 result.AppendError("Cannot disassemble around the current function "
323 "without a selected frame.\n");
324 result.SetStatus(eReturnStatusFailed);
325 return false;
327 Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
328 if (symbol) {
329 range.GetBaseAddress() = symbol->GetAddress();
330 range.SetByteSize(symbol->GetByteSize());
334 // Did the "m_options.frame_line" find a valid range already? If so skip
335 // the rest...
336 if (range.GetByteSize() == 0) {
337 if (m_options.at_pc) {
338 if (frame == nullptr) {
339 result.AppendError("Cannot disassemble around the current PC without "
340 "a selected frame.\n");
341 result.SetStatus(eReturnStatusFailed);
342 return false;
344 range.GetBaseAddress() = frame->GetFrameCodeAddress();
345 if (m_options.num_instructions == 0) {
346 // Disassembling at the PC always disassembles some number of
347 // instructions (not the whole function).
348 m_options.num_instructions = DEFAULT_DISASM_NUM_INS;
350 ranges.push_back(range);
351 } else {
352 range.GetBaseAddress().SetOffset(m_options.start_addr);
353 if (range.GetBaseAddress().IsValid()) {
354 if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
355 if (m_options.end_addr <= m_options.start_addr) {
356 result.AppendErrorWithFormat(
357 "End address before start address.\n");
358 result.SetStatus(eReturnStatusFailed);
359 return false;
361 range.SetByteSize(m_options.end_addr - m_options.start_addr);
363 ranges.push_back(range);
364 } else {
365 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS &&
366 target) {
367 if (!target->GetSectionLoadList().IsEmpty()) {
368 bool failed = false;
369 Address symbol_containing_address;
370 if (target->GetSectionLoadList().ResolveLoadAddress(
371 m_options.symbol_containing_addr,
372 symbol_containing_address)) {
373 ModuleSP module_sp(symbol_containing_address.GetModule());
374 SymbolContext sc;
375 bool resolve_tail_call_address = true; // PC can be one past the
376 // address range of the
377 // function.
378 module_sp->ResolveSymbolContextForAddress(
379 symbol_containing_address, eSymbolContextEverything, sc,
380 resolve_tail_call_address);
381 if (sc.function || sc.symbol) {
382 sc.GetAddressRange(eSymbolContextFunction |
383 eSymbolContextSymbol,
384 0, false, range);
385 } else {
386 failed = true;
388 } else {
389 failed = true;
391 if (failed) {
392 result.AppendErrorWithFormat(
393 "Could not find function bounds for address 0x%" PRIx64
394 "\n",
395 m_options.symbol_containing_addr);
396 result.SetStatus(eReturnStatusFailed);
397 return false;
399 ranges.push_back(range);
400 } else {
401 for (lldb::ModuleSP module_sp : target->GetImages().Modules()) {
402 lldb::addr_t file_addr = m_options.symbol_containing_addr;
403 Address file_address;
404 if (module_sp->ResolveFileAddress(file_addr, file_address)) {
405 SymbolContext sc;
406 bool resolve_tail_call_address = true; // PC can be one past
407 // the address range of
408 // the function.
409 module_sp->ResolveSymbolContextForAddress(
410 file_address, eSymbolContextEverything, sc,
411 resolve_tail_call_address);
412 if (sc.function || sc.symbol) {
413 sc.GetAddressRange(eSymbolContextFunction |
414 eSymbolContextSymbol,
415 0, false, range);
416 ranges.push_back(range);
424 } else
425 ranges.push_back(range);
427 if (m_options.num_instructions != 0) {
428 if (ranges.empty()) {
429 // The default action is to disassemble the current frame function.
430 if (frame) {
431 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
432 eSymbolContextSymbol));
433 if (sc.function)
434 range.GetBaseAddress() =
435 sc.function->GetAddressRange().GetBaseAddress();
436 else if (sc.symbol && sc.symbol->ValueIsAddress())
437 range.GetBaseAddress() = sc.symbol->GetAddress();
438 else
439 range.GetBaseAddress() = frame->GetFrameCodeAddress();
442 if (!range.GetBaseAddress().IsValid()) {
443 result.AppendError("invalid frame");
444 result.SetStatus(eReturnStatusFailed);
445 return false;
449 bool print_sc_header = ranges.size() > 1;
450 for (AddressRange cur_range : ranges) {
451 if (Disassembler::Disassemble(
452 GetDebugger(), m_options.arch, plugin_name, flavor_string,
453 m_exe_ctx, cur_range.GetBaseAddress(),
454 m_options.num_instructions, m_options.show_mixed,
455 m_options.show_mixed ? m_options.num_lines_context : 0, options,
456 result.GetOutputStream())) {
457 result.SetStatus(eReturnStatusSuccessFinishResult);
458 } else {
459 if (m_options.start_addr != LLDB_INVALID_ADDRESS)
460 result.AppendErrorWithFormat(
461 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
462 m_options.start_addr);
463 else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
464 result.AppendErrorWithFormat(
465 "Failed to disassemble memory in function at 0x%8.8" PRIx64
466 ".\n",
467 m_options.symbol_containing_addr);
468 result.SetStatus(eReturnStatusFailed);
471 if (print_sc_header)
472 result.AppendMessage("\n");
473 } else {
474 if (ranges.empty()) {
475 // The default action is to disassemble the current frame function.
476 if (frame) {
477 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
478 eSymbolContextSymbol));
479 if (sc.function)
480 range = sc.function->GetAddressRange();
481 else if (sc.symbol && sc.symbol->ValueIsAddress()) {
482 range.GetBaseAddress() = sc.symbol->GetAddress();
483 range.SetByteSize(sc.symbol->GetByteSize());
484 } else
485 range.GetBaseAddress() = frame->GetFrameCodeAddress();
486 } else {
487 result.AppendError("invalid frame");
488 result.SetStatus(eReturnStatusFailed);
489 return false;
491 ranges.push_back(range);
494 bool print_sc_header = ranges.size() > 1;
495 for (AddressRange cur_range : ranges) {
496 if (cur_range.GetByteSize() == 0)
497 cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
499 if (Disassembler::Disassemble(
500 GetDebugger(), m_options.arch, plugin_name, flavor_string,
501 m_exe_ctx, cur_range, m_options.num_instructions,
502 m_options.show_mixed,
503 m_options.show_mixed ? m_options.num_lines_context : 0, options,
504 result.GetOutputStream())) {
505 result.SetStatus(eReturnStatusSuccessFinishResult);
506 } else {
507 result.AppendErrorWithFormat(
508 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
509 cur_range.GetBaseAddress().GetLoadAddress(target));
510 result.SetStatus(eReturnStatusFailed);
512 if (print_sc_header)
513 result.AppendMessage("\n");
518 return result.Succeeded();