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