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>]", eCommandRequiresTarget), 216 m_options() {} 217 218 CommandObjectDisassemble::~CommandObjectDisassemble() = default; 219 220 bool CommandObjectDisassemble::DoExecute(Args &command, 221 CommandReturnObject &result) { 222 Target *target = &GetSelectedTarget(); 223 224 if (!m_options.arch.IsValid()) 225 m_options.arch = target->GetArchitecture(); 226 227 if (!m_options.arch.IsValid()) { 228 result.AppendError( 229 "use the --arch option or set the target architecture to disassemble"); 230 result.SetStatus(eReturnStatusFailed); 231 return false; 232 } 233 234 const char *plugin_name = m_options.GetPluginName(); 235 const char *flavor_string = m_options.GetFlavorString(); 236 237 DisassemblerSP disassembler = 238 Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name); 239 240 if (!disassembler) { 241 if (plugin_name) { 242 result.AppendErrorWithFormat( 243 "Unable to find Disassembler plug-in named '%s' that supports the " 244 "'%s' architecture.\n", 245 plugin_name, m_options.arch.GetArchitectureName()); 246 } else 247 result.AppendErrorWithFormat( 248 "Unable to find Disassembler plug-in for the '%s' architecture.\n", 249 m_options.arch.GetArchitectureName()); 250 result.SetStatus(eReturnStatusFailed); 251 return false; 252 } else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec( 253 m_options.arch, flavor_string)) 254 result.AppendWarningWithFormat( 255 "invalid disassembler flavor \"%s\", using default.\n", flavor_string); 256 257 result.SetStatus(eReturnStatusSuccessFinishResult); 258 259 if (!command.empty()) { 260 result.AppendErrorWithFormat( 261 "\"disassemble\" arguments are specified as options.\n"); 262 const int terminal_width = 263 GetCommandInterpreter().GetDebugger().GetTerminalWidth(); 264 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this, 265 terminal_width); 266 result.SetStatus(eReturnStatusFailed); 267 return false; 268 } 269 270 if (m_options.show_mixed && m_options.num_lines_context == 0) 271 m_options.num_lines_context = 2; 272 273 // Always show the PC in the disassembly 274 uint32_t options = Disassembler::eOptionMarkPCAddress; 275 276 // Mark the source line for the current PC only if we are doing mixed source 277 // and assembly 278 if (m_options.show_mixed) 279 options |= Disassembler::eOptionMarkPCSourceLine; 280 281 if (m_options.show_bytes) 282 options |= Disassembler::eOptionShowBytes; 283 284 if (m_options.raw) 285 options |= Disassembler::eOptionRawOuput; 286 287 if (!m_options.func_name.empty()) { 288 ConstString name(m_options.func_name.c_str()); 289 290 if (Disassembler::Disassemble( 291 GetDebugger(), m_options.arch, plugin_name, flavor_string, 292 m_exe_ctx, name, 293 nullptr, // Module * 294 m_options.num_instructions, m_options.show_mixed, 295 m_options.show_mixed ? m_options.num_lines_context : 0, options, 296 result.GetOutputStream())) { 297 result.SetStatus(eReturnStatusSuccessFinishResult); 298 } else { 299 result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n", 300 name.GetCString()); 301 result.SetStatus(eReturnStatusFailed); 302 } 303 } else { 304 std::vector<AddressRange> ranges; 305 AddressRange range; 306 StackFrame *frame = m_exe_ctx.GetFramePtr(); 307 if (m_options.frame_line) { 308 if (frame == nullptr) { 309 result.AppendError("Cannot disassemble around the current line without " 310 "a selected frame.\n"); 311 result.SetStatus(eReturnStatusFailed); 312 return false; 313 } 314 LineEntry pc_line_entry( 315 frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); 316 if (pc_line_entry.IsValid()) { 317 range = pc_line_entry.range; 318 } else { 319 m_options.at_pc = 320 true; // No line entry, so just disassemble around the current pc 321 m_options.show_mixed = false; 322 } 323 } else if (m_options.current_function) { 324 if (frame == nullptr) { 325 result.AppendError("Cannot disassemble around the current function " 326 "without a selected frame.\n"); 327 result.SetStatus(eReturnStatusFailed); 328 return false; 329 } 330 Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; 331 if (symbol) { 332 range.GetBaseAddress() = symbol->GetAddress(); 333 range.SetByteSize(symbol->GetByteSize()); 334 } 335 } 336 337 // Did the "m_options.frame_line" find a valid range already? If so skip 338 // the rest... 339 if (range.GetByteSize() == 0) { 340 if (m_options.at_pc) { 341 if (frame == nullptr) { 342 result.AppendError("Cannot disassemble around the current PC without " 343 "a selected frame.\n"); 344 result.SetStatus(eReturnStatusFailed); 345 return false; 346 } 347 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 348 if (m_options.num_instructions == 0) { 349 // Disassembling at the PC always disassembles some number of 350 // instructions (not the whole function). 351 m_options.num_instructions = DEFAULT_DISASM_NUM_INS; 352 } 353 ranges.push_back(range); 354 } else { 355 range.GetBaseAddress().SetOffset(m_options.start_addr); 356 if (range.GetBaseAddress().IsValid()) { 357 if (m_options.end_addr != LLDB_INVALID_ADDRESS) { 358 if (m_options.end_addr <= m_options.start_addr) { 359 result.AppendErrorWithFormat( 360 "End address before start address.\n"); 361 result.SetStatus(eReturnStatusFailed); 362 return false; 363 } 364 range.SetByteSize(m_options.end_addr - m_options.start_addr); 365 } 366 ranges.push_back(range); 367 } else { 368 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS && 369 target) { 370 if (!target->GetSectionLoadList().IsEmpty()) { 371 bool failed = false; 372 Address symbol_containing_address; 373 if (target->GetSectionLoadList().ResolveLoadAddress( 374 m_options.symbol_containing_addr, 375 symbol_containing_address)) { 376 ModuleSP module_sp(symbol_containing_address.GetModule()); 377 SymbolContext sc; 378 bool resolve_tail_call_address = true; // PC can be one past the 379 // address range of the 380 // function. 381 module_sp->ResolveSymbolContextForAddress( 382 symbol_containing_address, eSymbolContextEverything, sc, 383 resolve_tail_call_address); 384 if (sc.function || sc.symbol) { 385 sc.GetAddressRange(eSymbolContextFunction | 386 eSymbolContextSymbol, 387 0, false, range); 388 } else { 389 failed = true; 390 } 391 } else { 392 failed = true; 393 } 394 if (failed) { 395 result.AppendErrorWithFormat( 396 "Could not find function bounds for address 0x%" PRIx64 397 "\n", 398 m_options.symbol_containing_addr); 399 result.SetStatus(eReturnStatusFailed); 400 return false; 401 } 402 ranges.push_back(range); 403 } else { 404 for (lldb::ModuleSP module_sp : target->GetImages().Modules()) { 405 lldb::addr_t file_addr = m_options.symbol_containing_addr; 406 Address file_address; 407 if (module_sp->ResolveFileAddress(file_addr, file_address)) { 408 SymbolContext sc; 409 bool resolve_tail_call_address = true; // PC can be one past 410 // the address range of 411 // the function. 412 module_sp->ResolveSymbolContextForAddress( 413 file_address, eSymbolContextEverything, sc, 414 resolve_tail_call_address); 415 if (sc.function || sc.symbol) { 416 sc.GetAddressRange(eSymbolContextFunction | 417 eSymbolContextSymbol, 418 0, false, range); 419 ranges.push_back(range); 420 } 421 } 422 } 423 } 424 } 425 } 426 } 427 } else 428 ranges.push_back(range); 429 430 if (m_options.num_instructions != 0) { 431 if (ranges.empty()) { 432 // The default action is to disassemble the current frame function. 433 if (frame) { 434 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | 435 eSymbolContextSymbol)); 436 if (sc.function) 437 range.GetBaseAddress() = 438 sc.function->GetAddressRange().GetBaseAddress(); 439 else if (sc.symbol && sc.symbol->ValueIsAddress()) 440 range.GetBaseAddress() = sc.symbol->GetAddress(); 441 else 442 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 443 } 444 445 if (!range.GetBaseAddress().IsValid()) { 446 result.AppendError("invalid frame"); 447 result.SetStatus(eReturnStatusFailed); 448 return false; 449 } 450 } 451 452 bool print_sc_header = ranges.size() > 1; 453 for (AddressRange cur_range : ranges) { 454 if (Disassembler::Disassemble( 455 GetDebugger(), m_options.arch, plugin_name, flavor_string, 456 m_exe_ctx, cur_range.GetBaseAddress(), 457 m_options.num_instructions, m_options.show_mixed, 458 m_options.show_mixed ? m_options.num_lines_context : 0, options, 459 result.GetOutputStream())) { 460 result.SetStatus(eReturnStatusSuccessFinishResult); 461 } else { 462 if (m_options.start_addr != LLDB_INVALID_ADDRESS) 463 result.AppendErrorWithFormat( 464 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", 465 m_options.start_addr); 466 else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) 467 result.AppendErrorWithFormat( 468 "Failed to disassemble memory in function at 0x%8.8" PRIx64 469 ".\n", 470 m_options.symbol_containing_addr); 471 result.SetStatus(eReturnStatusFailed); 472 } 473 } 474 if (print_sc_header) 475 result.AppendMessage("\n"); 476 } else { 477 if (ranges.empty()) { 478 // The default action is to disassemble the current frame function. 479 if (frame) { 480 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | 481 eSymbolContextSymbol)); 482 if (sc.function) 483 range = sc.function->GetAddressRange(); 484 else if (sc.symbol && sc.symbol->ValueIsAddress()) { 485 range.GetBaseAddress() = sc.symbol->GetAddress(); 486 range.SetByteSize(sc.symbol->GetByteSize()); 487 } else 488 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 489 } else { 490 result.AppendError("invalid frame"); 491 result.SetStatus(eReturnStatusFailed); 492 return false; 493 } 494 ranges.push_back(range); 495 } 496 497 bool print_sc_header = ranges.size() > 1; 498 for (AddressRange cur_range : ranges) { 499 if (cur_range.GetByteSize() == 0) 500 cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); 501 502 if (Disassembler::Disassemble( 503 GetDebugger(), m_options.arch, plugin_name, flavor_string, 504 m_exe_ctx, cur_range, m_options.num_instructions, 505 m_options.show_mixed, 506 m_options.show_mixed ? m_options.num_lines_context : 0, options, 507 result.GetOutputStream())) { 508 result.SetStatus(eReturnStatusSuccessFinishResult); 509 } else { 510 result.AppendErrorWithFormat( 511 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", 512 cur_range.GetBaseAddress().GetLoadAddress(target)); 513 result.SetStatus(eReturnStatusFailed); 514 } 515 if (print_sc_header) 516 result.AppendMessage("\n"); 517 } 518 } 519 } 520 521 return result.Succeeded(); 522 } 523