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 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 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 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 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> 229 CommandObjectDisassemble::CommandOptions::GetDefinitions() { 230 return llvm::makeArrayRef(g_disassemble_options); 231 } 232 233 //------------------------------------------------------------------------- 234 // CommandObjectDisassemble 235 //------------------------------------------------------------------------- 236 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 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