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