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