1 //===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
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
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
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
) {
48 const int short_option
= m_getopt_table
[option_idx
].val
;
50 switch (short_option
) {
56 if (option_arg
.getAsInteger(0, num_lines_context
))
57 error
.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
58 option_arg
.str().c_str());
62 if (option_arg
.getAsInteger(0, num_instructions
))
63 error
.SetErrorStringWithFormat(
64 "invalid num of instructions string: \"%s\"",
65 option_arg
.str().c_str());
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;
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;
86 func_name
.assign(option_arg
);
87 some_location_specified
= true;
92 some_location_specified
= true;
97 // Disassemble the current source line kind of implies showing mixed source
100 some_location_specified
= true;
104 plugin_name
.assign(option_arg
);
109 execution_context
? execution_context
->GetTargetSP() : TargetSP();
110 if (target_sp
&& (target_sp
->GetArchitecture().GetTriple().getArch() ==
112 target_sp
->GetArchitecture().GetTriple().getArch() ==
113 llvm::Triple::x86_64
)) {
114 flavor_string
.assign(option_arg
);
116 error
.SetErrorStringWithFormat("Disassembler flavors are currently only "
117 "supported for x86 and x86_64 targets.");
126 current_function
= true;
127 some_location_specified
= true;
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
);
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;
147 llvm_unreachable("Unimplemented option");
153 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
154 ExecutionContext
*execution_context
) {
157 num_lines_context
= 0;
158 num_instructions
= 0;
160 current_function
= false;
163 start_addr
= LLDB_INVALID_ADDRESS
;
164 end_addr
= LLDB_INVALID_ADDRESS
;
165 symbol_containing_addr
= LLDB_INVALID_ADDRESS
;
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...
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());
182 flavor_string
.assign("default");
185 flavor_string
.assign("default");
188 some_location_specified
= false;
191 Status
CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
192 ExecutionContext
*execution_context
) {
193 if (!some_location_specified
)
194 current_function
= true;
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 "
212 "disassemble [<cmd-options>]", eCommandRequiresTarget
),
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()) {
226 "use the --arch option or set the target architecture to disassemble");
227 result
.SetStatus(eReturnStatusFailed
);
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
);
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());
244 result
.AppendErrorWithFormat(
245 "Unable to find Disassembler plug-in for the '%s' architecture.\n",
246 m_options
.arch
.GetArchitectureName());
247 result
.SetStatus(eReturnStatusFailed
);
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,
263 result
.SetStatus(eReturnStatusFailed
);
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
275 if (m_options
.show_mixed
)
276 options
|= Disassembler::eOptionMarkPCSourceLine
;
278 if (m_options
.show_bytes
)
279 options
|= Disassembler::eOptionShowBytes
;
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
,
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
);
296 result
.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
298 result
.SetStatus(eReturnStatusFailed
);
301 std::vector
<AddressRange
> ranges
;
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
);
311 LineEntry
pc_line_entry(
312 frame
->GetSymbolContext(eSymbolContextLineEntry
).line_entry
);
313 if (pc_line_entry
.IsValid()) {
314 range
= pc_line_entry
.range
;
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
);
327 Symbol
*symbol
= frame
->GetSymbolContext(eSymbolContextSymbol
).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
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
);
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
);
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
);
361 range
.SetByteSize(m_options
.end_addr
- m_options
.start_addr
);
363 ranges
.push_back(range
);
365 if (m_options
.symbol_containing_addr
!= LLDB_INVALID_ADDRESS
&&
367 if (!target
->GetSectionLoadList().IsEmpty()) {
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());
375 bool resolve_tail_call_address
= true; // PC can be one past the
376 // address range of the
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
,
392 result
.AppendErrorWithFormat(
393 "Could not find function bounds for address 0x%" PRIx64
395 m_options
.symbol_containing_addr
);
396 result
.SetStatus(eReturnStatusFailed
);
399 ranges
.push_back(range
);
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
)) {
406 bool resolve_tail_call_address
= true; // PC can be one past
407 // the address range of
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
,
416 ranges
.push_back(range
);
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.
431 SymbolContext
sc(frame
->GetSymbolContext(eSymbolContextFunction
|
432 eSymbolContextSymbol
));
434 range
.GetBaseAddress() =
435 sc
.function
->GetAddressRange().GetBaseAddress();
436 else if (sc
.symbol
&& sc
.symbol
->ValueIsAddress())
437 range
.GetBaseAddress() = sc
.symbol
->GetAddress();
439 range
.GetBaseAddress() = frame
->GetFrameCodeAddress();
442 if (!range
.GetBaseAddress().IsValid()) {
443 result
.AppendError("invalid frame");
444 result
.SetStatus(eReturnStatusFailed
);
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
);
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
467 m_options
.symbol_containing_addr
);
468 result
.SetStatus(eReturnStatusFailed
);
472 result
.AppendMessage("\n");
474 if (ranges
.empty()) {
475 // The default action is to disassemble the current frame function.
477 SymbolContext
sc(frame
->GetSymbolContext(eSymbolContextFunction
|
478 eSymbolContextSymbol
));
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());
485 range
.GetBaseAddress() = frame
->GetFrameCodeAddress();
487 result
.AppendError("invalid frame");
488 result
.SetStatus(eReturnStatusFailed
);
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
);
507 result
.AppendErrorWithFormat(
508 "Failed to disassemble memory at 0x%8.8" PRIx64
".\n",
509 cur_range
.GetBaseAddress().GetLoadAddress(target
));
510 result
.SetStatus(eReturnStatusFailed
);
513 result
.AppendMessage("\n");
518 return result
.Succeeded();