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 { LLDB_OPT_SET_ALL, false, "bytes" , 'b', OptionParser::eNoArgument , nullptr, nullptr, 0, eArgTypeNone, "Show opcode bytes when disassembling."}, 247 { LLDB_OPT_SET_ALL, false, "context" , 'C', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeNumLines, "Number of context lines of source to show."}, 248 { LLDB_OPT_SET_ALL, false, "mixed" , 'm', OptionParser::eNoArgument , nullptr, nullptr, 0, eArgTypeNone, "Enable mixed source and assembly display."}, 249 { LLDB_OPT_SET_ALL, false, "raw" , 'r', OptionParser::eNoArgument , nullptr, nullptr, 0, eArgTypeNone, "Print raw disassembly with no symbol information."}, 250 { LLDB_OPT_SET_ALL, false, "plugin" , 'P', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use."}, 251 { LLDB_OPT_SET_ALL, false, "flavor" , 'F', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. " 252 "Currently the only valid options are default, and for Intel" 253 " architectures, att and intel."}, 254 { LLDB_OPT_SET_ALL, false, "arch" , 'A', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeArchitecture,"Specify the architecture to use from cross disassembly."}, 255 { LLDB_OPT_SET_1 | 256 LLDB_OPT_SET_2 , true , "start-address", 's', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeAddressOrExpression,"Address at which to start disassembling."}, 257 { LLDB_OPT_SET_1 , false, "end-address" , 'e', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling."}, 258 { LLDB_OPT_SET_2 | 259 LLDB_OPT_SET_3 | 260 LLDB_OPT_SET_4 | 261 LLDB_OPT_SET_5 , false, "count" , 'c', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeNumLines, "Number of instructions to display."}, 262 { LLDB_OPT_SET_3 , false, "name" , 'n', OptionParser::eRequiredArgument , nullptr, nullptr, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, 263 "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 table information, else disassemble around the pc."}, 267 { LLDB_OPT_SET_7 , false, "address" , 'a', OptionParser::eRequiredArgument , nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address."}, 268 { 0 , false, nullptr , 0, 0 , nullptr, nullptr, 0, eArgTypeNone, nullptr } 269 }; 270 271 //------------------------------------------------------------------------- 272 // CommandObjectDisassemble 273 //------------------------------------------------------------------------- 274 275 CommandObjectDisassemble::CommandObjectDisassemble(CommandInterpreter &interpreter) 276 : CommandObjectParsed(interpreter, "disassemble", "Disassemble specified instructions in the current target. " 277 "Defaults to the current function for the current thread and " 278 "stack frame.", 279 "disassemble [<cmd-options>]"), 280 m_options() 281 { 282 } 283 284 CommandObjectDisassemble::~CommandObjectDisassemble() = default; 285 286 bool 287 CommandObjectDisassemble::DoExecute (Args& command, CommandReturnObject &result) 288 { 289 Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); 290 if (target == nullptr) 291 { 292 result.AppendError ("invalid target, create a debug target using the 'target create' command"); 293 result.SetStatus (eReturnStatusFailed); 294 return false; 295 } 296 if (!m_options.arch.IsValid()) 297 m_options.arch = target->GetArchitecture(); 298 299 if (!m_options.arch.IsValid()) 300 { 301 result.AppendError ("use the --arch option or set the target architecture to disassemble"); 302 result.SetStatus (eReturnStatusFailed); 303 return false; 304 } 305 306 const char *plugin_name = m_options.GetPluginName (); 307 const char *flavor_string = m_options.GetFlavorString(); 308 309 DisassemblerSP disassembler = Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name); 310 311 if (!disassembler) 312 { 313 if (plugin_name) 314 { 315 result.AppendErrorWithFormat ("Unable to find Disassembler plug-in named '%s' that supports the '%s' architecture.\n", 316 plugin_name, 317 m_options.arch.GetArchitectureName()); 318 } 319 else 320 result.AppendErrorWithFormat ("Unable to find Disassembler plug-in for the '%s' architecture.\n", 321 m_options.arch.GetArchitectureName()); 322 result.SetStatus (eReturnStatusFailed); 323 return false; 324 } 325 else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec(m_options.arch, flavor_string)) 326 result.AppendWarningWithFormat("invalid disassembler flavor \"%s\", using default.\n", flavor_string); 327 328 result.SetStatus (eReturnStatusSuccessFinishResult); 329 330 if (command.GetArgumentCount() != 0) 331 { 332 result.AppendErrorWithFormat ("\"disassemble\" arguments are specified as options.\n"); 333 const int terminal_width = 334 GetCommandInterpreter().GetDebugger().GetTerminalWidth(); 335 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this, 336 terminal_width); 337 result.SetStatus (eReturnStatusFailed); 338 return false; 339 } 340 341 if (m_options.show_mixed && m_options.num_lines_context == 0) 342 m_options.num_lines_context = 1; 343 344 // Always show the PC in the disassembly 345 uint32_t options = Disassembler::eOptionMarkPCAddress; 346 347 // Mark the source line for the current PC only if we are doing mixed source and assembly 348 if (m_options.show_mixed) 349 options |= Disassembler::eOptionMarkPCSourceLine; 350 351 if (m_options.show_bytes) 352 options |= Disassembler::eOptionShowBytes; 353 354 if (m_options.raw) 355 options |= Disassembler::eOptionRawOuput; 356 357 if (!m_options.func_name.empty()) 358 { 359 ConstString name(m_options.func_name.c_str()); 360 361 if (Disassembler::Disassemble(m_interpreter.GetDebugger(), 362 m_options.arch, 363 plugin_name, 364 flavor_string, 365 m_exe_ctx, 366 name, 367 nullptr, // Module * 368 m_options.num_instructions, 369 m_options.show_mixed ? m_options.num_lines_context : 0, 370 options, 371 result.GetOutputStream())) 372 { 373 result.SetStatus (eReturnStatusSuccessFinishResult); 374 } 375 else 376 { 377 result.AppendErrorWithFormat ("Unable to find symbol with name '%s'.\n", name.GetCString()); 378 result.SetStatus (eReturnStatusFailed); 379 } 380 } 381 else 382 { 383 std::vector<AddressRange> ranges; 384 AddressRange range; 385 StackFrame *frame = m_exe_ctx.GetFramePtr(); 386 if (m_options.frame_line) 387 { 388 if (frame == nullptr) 389 { 390 result.AppendError ("Cannot disassemble around the current line without a selected frame.\n"); 391 result.SetStatus (eReturnStatusFailed); 392 return false; 393 } 394 LineEntry pc_line_entry (frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); 395 if (pc_line_entry.IsValid()) 396 { 397 range = pc_line_entry.range; 398 } 399 else 400 { 401 m_options.at_pc = true; // No line entry, so just disassemble around the current pc 402 m_options.show_mixed = false; 403 } 404 } 405 else if (m_options.current_function) 406 { 407 if (frame == nullptr) 408 { 409 result.AppendError ("Cannot disassemble around the current function without a selected frame.\n"); 410 result.SetStatus (eReturnStatusFailed); 411 return false; 412 } 413 Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; 414 if (symbol) 415 { 416 range.GetBaseAddress() = symbol->GetAddress(); 417 range.SetByteSize(symbol->GetByteSize()); 418 } 419 } 420 421 // Did the "m_options.frame_line" find a valid range already? If so 422 // skip the rest... 423 if (range.GetByteSize() == 0) 424 { 425 if (m_options.at_pc) 426 { 427 if (frame == nullptr) 428 { 429 result.AppendError ("Cannot disassemble around the current PC without a selected frame.\n"); 430 result.SetStatus (eReturnStatusFailed); 431 return false; 432 } 433 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 434 if (m_options.num_instructions == 0) 435 { 436 // Disassembling at the PC always disassembles some number of instructions (not the whole function). 437 m_options.num_instructions = DEFAULT_DISASM_NUM_INS; 438 } 439 ranges.push_back(range); 440 } 441 else 442 { 443 range.GetBaseAddress().SetOffset (m_options.start_addr); 444 if (range.GetBaseAddress().IsValid()) 445 { 446 if (m_options.end_addr != LLDB_INVALID_ADDRESS) 447 { 448 if (m_options.end_addr <= m_options.start_addr) 449 { 450 result.AppendErrorWithFormat ("End address before start address.\n"); 451 result.SetStatus (eReturnStatusFailed); 452 return false; 453 } 454 range.SetByteSize (m_options.end_addr - m_options.start_addr); 455 } 456 ranges.push_back(range); 457 } 458 else 459 { 460 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS 461 && target) 462 { 463 if (!target->GetSectionLoadList().IsEmpty()) 464 { 465 bool failed = false; 466 Address symbol_containing_address; 467 if (target->GetSectionLoadList().ResolveLoadAddress (m_options.symbol_containing_addr, symbol_containing_address)) 468 { 469 ModuleSP module_sp (symbol_containing_address.GetModule()); 470 SymbolContext sc; 471 bool resolve_tail_call_address = true; // PC can be one past the address range of the function. 472 module_sp->ResolveSymbolContextForAddress (symbol_containing_address, eSymbolContextEverything, sc, 473 resolve_tail_call_address); 474 if (sc.function || sc.symbol) 475 { 476 sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range); 477 } 478 else 479 { 480 failed = true; 481 } 482 } 483 else 484 { 485 failed = true; 486 } 487 if (failed) 488 { 489 result.AppendErrorWithFormat ("Could not find function bounds for address 0x%" PRIx64 "\n", m_options.symbol_containing_addr); 490 result.SetStatus (eReturnStatusFailed); 491 return false; 492 } 493 ranges.push_back(range); 494 } 495 else 496 { 497 for (lldb::ModuleSP module_sp : target->GetImages().Modules()) 498 { 499 lldb::addr_t file_addr = m_options.symbol_containing_addr; 500 Address file_address; 501 if (module_sp->ResolveFileAddress(file_addr, file_address)) 502 { 503 SymbolContext sc; 504 bool resolve_tail_call_address = true; // PC can be one past the address range of the function. 505 module_sp->ResolveSymbolContextForAddress (file_address, eSymbolContextEverything, sc, resolve_tail_call_address); 506 if (sc.function || sc.symbol) 507 { 508 sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, range); 509 ranges.push_back(range); 510 } 511 } 512 } 513 } 514 } 515 } 516 } 517 } 518 else 519 ranges.push_back(range); 520 521 if (m_options.num_instructions != 0) 522 { 523 if (ranges.empty()) 524 { 525 // The default action is to disassemble the current frame function. 526 if (frame) 527 { 528 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); 529 if (sc.function) 530 range.GetBaseAddress() = sc.function->GetAddressRange().GetBaseAddress(); 531 else if (sc.symbol && sc.symbol->ValueIsAddress()) 532 range.GetBaseAddress() = sc.symbol->GetAddress(); 533 else 534 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 535 } 536 537 if (!range.GetBaseAddress().IsValid()) 538 { 539 result.AppendError ("invalid frame"); 540 result.SetStatus (eReturnStatusFailed); 541 return false; 542 } 543 } 544 545 bool print_sc_header = ranges.size() > 1; 546 for (AddressRange cur_range : ranges) 547 { 548 if (Disassembler::Disassemble (m_interpreter.GetDebugger(), 549 m_options.arch, 550 plugin_name, 551 flavor_string, 552 m_exe_ctx, 553 cur_range.GetBaseAddress(), 554 m_options.num_instructions, 555 m_options.show_mixed ? m_options.num_lines_context : 0, 556 options, 557 result.GetOutputStream())) 558 { 559 result.SetStatus (eReturnStatusSuccessFinishResult); 560 } 561 else 562 { 563 if (m_options.start_addr != LLDB_INVALID_ADDRESS) 564 result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); 565 else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) 566 result.AppendErrorWithFormat ("Failed to disassemble memory in function at 0x%8.8" PRIx64 ".\n", m_options.symbol_containing_addr); 567 result.SetStatus (eReturnStatusFailed); 568 } 569 } 570 if (print_sc_header) 571 result.AppendMessage("\n"); 572 } 573 else 574 { 575 if (ranges.empty()) 576 { 577 // The default action is to disassemble the current frame function. 578 if (frame) 579 { 580 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol)); 581 if (sc.function) 582 range = sc.function->GetAddressRange(); 583 else if (sc.symbol && sc.symbol->ValueIsAddress()) 584 { 585 range.GetBaseAddress() = sc.symbol->GetAddress(); 586 range.SetByteSize (sc.symbol->GetByteSize()); 587 } 588 else 589 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 590 } 591 else 592 { 593 result.AppendError ("invalid frame"); 594 result.SetStatus (eReturnStatusFailed); 595 return false; 596 } 597 ranges.push_back(range); 598 } 599 600 bool print_sc_header = ranges.size() > 1; 601 for (AddressRange cur_range : ranges) 602 { 603 if (cur_range.GetByteSize() == 0) 604 cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); 605 606 if (Disassembler::Disassemble (m_interpreter.GetDebugger(), 607 m_options.arch, 608 plugin_name, 609 flavor_string, 610 m_exe_ctx, 611 cur_range, 612 m_options.num_instructions, 613 m_options.show_mixed ? m_options.num_lines_context : 0, 614 options, 615 result.GetOutputStream())) 616 { 617 result.SetStatus (eReturnStatusSuccessFinishResult); 618 } 619 else 620 { 621 result.AppendErrorWithFormat ("Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", m_options.start_addr); 622 result.SetStatus (eReturnStatusFailed); 623 } 624 if (print_sc_header) 625 result.AppendMessage("\n"); 626 } 627 } 628 } 629 630 return result.Succeeded(); 631 } 632