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