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