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