diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/tools/llvm-objdump | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) |
Diffstat (limited to 'llvm/tools/llvm-objdump')
-rw-r--r-- | llvm/tools/llvm-objdump/COFFDump.cpp | 16 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/ELFDump.cpp | 48 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/MachODump.cpp | 488 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/MachODump.h | 12 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/ObjdumpOpts.td | 47 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/OffloadDump.cpp | 72 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/OtoolOpts.td | 7 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/SourcePrinter.cpp | 14 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/SourcePrinter.h | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/XCOFFDump.cpp | 18 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/XCOFFDump.h | 4 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.cpp | 530 | ||||
-rw-r--r-- | llvm/tools/llvm-objdump/llvm-objdump.h | 6 |
13 files changed, 925 insertions, 338 deletions
diff --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp index e65762e02022..3bc7d3ce33b0 100644 --- a/llvm/tools/llvm-objdump/COFFDump.cpp +++ b/llvm/tools/llvm-objdump/COFFDump.cpp @@ -10,7 +10,7 @@ /// This file implements the COFF-specific dumper for llvm-objdump. /// It outputs the Win64 EH data structures as plain text. /// The encoding of the unwind codes is described in MSDN: -/// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx +/// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64 /// //===----------------------------------------------------------------------===// @@ -102,7 +102,7 @@ void COFFDumper::printPEHeader(const PEHeader &Hdr) const { }; printU16("Magic", Hdr.Magic, "%04x"); - printOptionalEnumName(Hdr.Magic, makeArrayRef(PEHeaderMagic)); + printOptionalEnumName(Hdr.Magic, ArrayRef(PEHeaderMagic)); outs() << '\n'; print("MajorLinkerVersion", Hdr.MajorLinkerVersion); print("MinorLinkerVersion", Hdr.MinorLinkerVersion); @@ -127,7 +127,7 @@ void COFFDumper::printPEHeader(const PEHeader &Hdr) const { printU32("SizeOfHeaders", Hdr.SizeOfHeaders, "%08x\n"); printU32("CheckSum", Hdr.CheckSum, "%08x\n"); printU16("Subsystem", Hdr.Subsystem, "%08x"); - printOptionalEnumName(Hdr.Subsystem, makeArrayRef(PEWindowsSubsystem)); + printOptionalEnumName(Hdr.Subsystem, ArrayRef(PEWindowsSubsystem)); outs() << '\n'; printU16("DllCharacteristics", Hdr.DLLCharacteristics, "%08x\n"); @@ -173,7 +173,7 @@ void COFFDumper::printPEHeader(const PEHeader &Hdr) const { "Reserved", }; outs() << "\nThe Data Directory\n"; - for (uint32_t I = 0; I != array_lengthof(DirName); ++I) { + for (uint32_t I = 0; I != std::size(DirName); ++I) { uint32_t Addr = 0, Size = 0; if (const data_directory *Data = Obj.getDataDirectory(I)) { Addr = Data->RelativeVirtualAddress; @@ -194,6 +194,8 @@ static StringRef getUnwindCodeTypeName(uint8_t Code) { case UOP_SetFPReg: return "UOP_SetFPReg"; case UOP_SaveNonVol: return "UOP_SaveNonVol"; case UOP_SaveNonVolBig: return "UOP_SaveNonVolBig"; + case UOP_Epilog: return "UOP_Epilog"; + case UOP_SpareCode: return "UOP_SpareCode"; case UOP_SaveXMM128: return "UOP_SaveXMM128"; case UOP_SaveXMM128Big: return "UOP_SaveXMM128Big"; case UOP_PushMachFrame: return "UOP_PushMachFrame"; @@ -234,9 +236,11 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) { return 1; case UOP_SaveNonVol: case UOP_SaveXMM128: + case UOP_Epilog: return 2; case UOP_SaveNonVolBig: case UOP_SaveXMM128Big: + case UOP_SpareCode: return 3; case UOP_AllocLarge: return (UnwindCode.getOpInfo() == 0) ? 2 : 3; @@ -305,7 +309,7 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) { << " remaining in buffer"; return ; } - printUnwindCode(makeArrayRef(I, E)); + printUnwindCode(ArrayRef(I, E)); I += UsedSlots; } } @@ -651,7 +655,7 @@ static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) { if (UI->NumCodes) outs() << " Unwind Codes:\n"; - printAllUnwindCodes(makeArrayRef(&UI->UnwindCodes[0], UI->NumCodes)); + printAllUnwindCodes(ArrayRef(&UI->UnwindCodes[0], UI->NumCodes)); outs() << "\n"; outs().flush(); diff --git a/llvm/tools/llvm-objdump/ELFDump.cpp b/llvm/tools/llvm-objdump/ELFDump.cpp index ca73dafe2b8e..b98b45e3015a 100644 --- a/llvm/tools/llvm-objdump/ELFDump.cpp +++ b/llvm/tools/llvm-objdump/ELFDump.cpp @@ -248,13 +248,16 @@ static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) { outs() << " NOTE "; break; case ELF::PT_OPENBSD_BOOTDATA: - outs() << " OPENBSD_BOOTDATA "; + outs() << "OPENBSD_BOOTDATA "; + break; + case ELF::PT_OPENBSD_MUTABLE: + outs() << "OPENBSD_MUTABLE "; break; case ELF::PT_OPENBSD_RANDOMIZE: - outs() << " OPENBSD_RANDOMIZE "; + outs() << "OPENBSD_RANDOMIZE "; break; case ELF::PT_OPENBSD_WXNEEDED: - outs() << " OPENBSD_WXNEEDED "; + outs() << "OPENBSD_WXNEEDED "; break; case ELF::PT_PHDR: outs() << " PHDR "; @@ -282,27 +285,28 @@ static void printProgramHeaders(const ELFFile<ELFT> &Obj, StringRef FileName) { } template <class ELFT> -static void printSymbolVersionDependency(ArrayRef<uint8_t> Contents, - StringRef StrTab) { +static void printSymbolVersionDependency(StringRef FileName, + const ELFFile<ELFT> &Obj, + const typename ELFT::Shdr &Sec) { outs() << "\nVersion References:\n"; - const uint8_t *Buf = Contents.data(); - while (Buf) { - auto *Verneed = reinterpret_cast<const typename ELFT::Verneed *>(Buf); - outs() << " required from " - << StringRef(StrTab.drop_front(Verneed->vn_file).data()) << ":\n"; + auto WarningHandler = [&](const Twine &Msg) { + reportWarning(Msg, FileName); + return Error::success(); + }; + Expected<std::vector<VerNeed>> V = + Obj.getVersionDependencies(Sec, WarningHandler); + if (!V) { + reportWarning(toString(V.takeError()), FileName); + return; + } - const uint8_t *BufAux = Buf + Verneed->vn_aux; - while (BufAux) { - auto *Vernaux = reinterpret_cast<const typename ELFT::Vernaux *>(BufAux); - outs() << " " - << format("0x%08" PRIx32 " ", (uint32_t)Vernaux->vna_hash) - << format("0x%02" PRIx16 " ", (uint16_t)Vernaux->vna_flags) - << format("%02" PRIu16 " ", (uint16_t)Vernaux->vna_other) - << StringRef(StrTab.drop_front(Vernaux->vna_name).data()) << '\n'; - BufAux = Vernaux->vna_next ? BufAux + Vernaux->vna_next : nullptr; - } - Buf = Verneed->vn_next ? Buf + Verneed->vn_next : nullptr; + raw_fd_ostream &OS = outs(); + for (const VerNeed &VN : *V) { + OS << " required from " << VN.File << ":\n"; + for (const VernAux &Aux : VN.AuxV) + OS << format(" 0x%08x 0x%02x %02u %s\n", Aux.Hash, Aux.Flags, + Aux.Other, Aux.Name.c_str()); } } @@ -355,7 +359,7 @@ static void printSymbolVersionInfo(const ELFFile<ELFT> &Elf, StringRef StrTab = unwrapOrError(Elf.getStringTable(*StrTabSec), FileName); if (Shdr.sh_type == ELF::SHT_GNU_verneed) - printSymbolVersionDependency<ELFT>(Contents, StrTab); + printSymbolVersionDependency<ELFT>(FileName, Elf, Shdr); else printSymbolVersionDefinition<ELFT>(Shdr, Contents, StrTab); } diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index cdbecd5ec243..fadc8367a8c1 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -78,9 +78,11 @@ bool objdump::UniversalHeaders; static bool ArchiveMemberOffsets; bool objdump::IndirectSymbols; bool objdump::DataInCode; -bool objdump::FunctionStarts; +FunctionStartsMode objdump::FunctionStartsType = + objdump::FunctionStartsMode::None; bool objdump::LinkOptHints; bool objdump::InfoPlist; +bool objdump::ChainedFixups; bool objdump::DyldInfo; bool objdump::DylibsUsed; bool objdump::DylibId; @@ -93,6 +95,8 @@ static std::vector<std::string> ArchFlags; static bool ArchAll = false; static std::string ThumbTripleName; +static StringRef ordinalName(const object::MachOObjectFile *, int); + void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) { FirstPrivateHeader = InputArgs.hasArg(OBJDUMP_private_header); ExportsTrie = InputArgs.hasArg(OBJDUMP_exports_trie); @@ -109,9 +113,18 @@ void objdump::parseMachOOptions(const llvm::opt::InputArgList &InputArgs) { ArchiveMemberOffsets = InputArgs.hasArg(OBJDUMP_archive_member_offsets); IndirectSymbols = InputArgs.hasArg(OBJDUMP_indirect_symbols); DataInCode = InputArgs.hasArg(OBJDUMP_data_in_code); - FunctionStarts = InputArgs.hasArg(OBJDUMP_function_starts); + if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_function_starts_EQ)) { + FunctionStartsType = StringSwitch<FunctionStartsMode>(A->getValue()) + .Case("addrs", FunctionStartsMode::Addrs) + .Case("names", FunctionStartsMode::Names) + .Case("both", FunctionStartsMode::Both) + .Default(FunctionStartsMode::None); + if (FunctionStartsType == FunctionStartsMode::None) + invalidArgValue(A); + } LinkOptHints = InputArgs.hasArg(OBJDUMP_link_opt_hints); InfoPlist = InputArgs.hasArg(OBJDUMP_info_plist); + ChainedFixups = InputArgs.hasArg(OBJDUMP_chained_fixups); DyldInfo = InputArgs.hasArg(OBJDUMP_dyld_info); DylibsUsed = InputArgs.hasArg(OBJDUMP_dylibs_used); DylibId = InputArgs.hasArg(OBJDUMP_dylib_id); @@ -242,19 +255,19 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, case MachO::DICE_KIND_DATA: if (Length >= 4) { if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 4), outs()); + dumpBytes(ArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; Size = 4; } else if (Length >= 2) { if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 2), outs()); + dumpBytes(ArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 2), outs()); + dumpBytes(ArrayRef(bytes, 2), outs()); Value = bytes[0]; outs() << "\t.byte " << Value; Size = 1; @@ -266,14 +279,14 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, break; case MachO::DICE_KIND_JUMP_TABLE8: if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 1), outs()); + dumpBytes(ArrayRef(bytes, 1), outs()); Value = bytes[0]; outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 2), outs()); + dumpBytes(ArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) << "\t@ KIND_JUMP_TABLE16\n"; @@ -282,7 +295,7 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: if (ShowRawInsn) - dumpBytes(makeArrayRef(bytes, 4), outs()); + dumpBytes(ArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; if (Kind == MachO::DICE_KIND_JUMP_TABLE32) @@ -1076,12 +1089,39 @@ static void PrintFunctionStarts(MachOObjectFile *O) { } } + DenseMap<uint64_t, StringRef> SymbolNames; + if (FunctionStartsType == FunctionStartsMode::Names || + FunctionStartsType == FunctionStartsMode::Both) { + for (SymbolRef Sym : O->symbols()) { + if (Expected<uint64_t> Addr = Sym.getAddress()) { + if (Expected<StringRef> Name = Sym.getName()) { + SymbolNames[*Addr] = *Name; + } + } + } + } + for (uint64_t S : FunctionStarts) { uint64_t Addr = BaseSegmentAddress + S; - if (O->is64Bit()) - outs() << format("%016" PRIx64, Addr) << "\n"; - else - outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)) << "\n"; + if (FunctionStartsType == FunctionStartsMode::Names) { + auto It = SymbolNames.find(Addr); + if (It != SymbolNames.end()) + outs() << It->second << "\n"; + } else { + if (O->is64Bit()) + outs() << format("%016" PRIx64, Addr); + else + outs() << format("%08" PRIx32, static_cast<uint32_t>(Addr)); + + if (FunctionStartsType == FunctionStartsMode::Both) { + auto It = SymbolNames.find(Addr); + if (It != SymbolNames.end()) + outs() << " " << It->second; + else + outs() << " ?"; + } + outs() << "\n"; + } } } @@ -1184,18 +1224,209 @@ static void PrintLinkOptHints(MachOObjectFile *O) { } } -static void printMachOChainedFixups(object::MachOObjectFile *Obj) { - Error Err = Error::success(); - for (const object::MachOChainedFixupEntry &Entry : Obj->fixupTable(Err)) { - (void)Entry; +static SmallVector<std::string> GetSegmentNames(object::MachOObjectFile *O) { + SmallVector<std::string> Ret; + for (const MachOObjectFile::LoadCommandInfo &Command : O->load_commands()) { + if (Command.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command SLC = O->getSegmentLoadCommand(Command); + Ret.push_back(SLC.segname); + } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 SLC = O->getSegment64LoadCommand(Command); + Ret.push_back(SLC.segname); + } } - if (Err) - reportError(std::move(Err), Obj->getFileName()); + return Ret; +} + +static void +PrintChainedFixupsHeader(const MachO::dyld_chained_fixups_header &H) { + outs() << "chained fixups header (LC_DYLD_CHAINED_FIXUPS)\n"; + outs() << " fixups_version = " << H.fixups_version << '\n'; + outs() << " starts_offset = " << H.starts_offset << '\n'; + outs() << " imports_offset = " << H.imports_offset << '\n'; + outs() << " symbols_offset = " << H.symbols_offset << '\n'; + outs() << " imports_count = " << H.imports_count << '\n'; + + outs() << " imports_format = " << H.imports_format; + switch (H.imports_format) { + case llvm::MachO::DYLD_CHAINED_IMPORT: + outs() << " (DYLD_CHAINED_IMPORT)"; + break; + case llvm::MachO::DYLD_CHAINED_IMPORT_ADDEND: + outs() << " (DYLD_CHAINED_IMPORT_ADDEND)"; + break; + case llvm::MachO::DYLD_CHAINED_IMPORT_ADDEND64: + outs() << " (DYLD_CHAINED_IMPORT_ADDEND64)"; + break; + } + outs() << '\n'; + + outs() << " symbols_format = " << H.symbols_format; + if (H.symbols_format == llvm::MachO::DYLD_CHAINED_SYMBOL_ZLIB) + outs() << " (zlib compressed)"; + outs() << '\n'; +} + +static constexpr std::array<StringRef, 13> PointerFormats{ + "DYLD_CHAINED_PTR_ARM64E", + "DYLD_CHAINED_PTR_64", + "DYLD_CHAINED_PTR_32", + "DYLD_CHAINED_PTR_32_CACHE", + "DYLD_CHAINED_PTR_32_FIRMWARE", + "DYLD_CHAINED_PTR_64_OFFSET", + "DYLD_CHAINED_PTR_ARM64E_KERNEL", + "DYLD_CHAINED_PTR_64_KERNEL_CACHE", + "DYLD_CHAINED_PTR_ARM64E_USERLAND", + "DYLD_CHAINED_PTR_ARM64E_FIRMWARE", + "DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE", + "DYLD_CHAINED_PTR_ARM64E_USERLAND24", +}; + +static void PrintChainedFixupsSegment(const ChainedFixupsSegment &Segment, + StringRef SegName) { + outs() << "chained starts in segment " << Segment.SegIdx << " (" << SegName + << ")\n"; + outs() << " size = " << Segment.Header.size << '\n'; + outs() << " page_size = " << format("0x%0" PRIx16, Segment.Header.page_size) + << '\n'; + + outs() << " pointer_format = " << Segment.Header.pointer_format; + if ((Segment.Header.pointer_format - 1) < + MachO::DYLD_CHAINED_PTR_ARM64E_USERLAND24) + outs() << " (" << PointerFormats[Segment.Header.pointer_format - 1] << ")"; + outs() << '\n'; + + outs() << " segment_offset = " + << format("0x%0" PRIx64, Segment.Header.segment_offset) << '\n'; + outs() << " max_valid_pointer = " << Segment.Header.max_valid_pointer + << '\n'; + outs() << " page_count = " << Segment.Header.page_count << '\n'; + for (auto [Index, PageStart] : enumerate(Segment.PageStarts)) { + outs() << " page_start[" << Index << "] = " << PageStart; + // FIXME: Support DYLD_CHAINED_PTR_START_MULTI (32-bit only) + if (PageStart == MachO::DYLD_CHAINED_PTR_START_NONE) + outs() << " (DYLD_CHAINED_PTR_START_NONE)"; + outs() << '\n'; + } +} + +static void PrintChainedFixupTarget(ChainedFixupTarget &Target, size_t Idx, + int Format, MachOObjectFile *O) { + if (Format == MachO::DYLD_CHAINED_IMPORT) + outs() << "dyld chained import"; + else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND) + outs() << "dyld chained import addend"; + else if (Format == MachO::DYLD_CHAINED_IMPORT_ADDEND64) + outs() << "dyld chained import addend64"; + // FIXME: otool prints the encoded value as well. + outs() << '[' << Idx << "]\n"; + + outs() << " lib_ordinal = " << Target.libOrdinal() << " (" + << ordinalName(O, Target.libOrdinal()) << ")\n"; + outs() << " weak_import = " << Target.weakImport() << '\n'; + outs() << " name_offset = " << Target.nameOffset() << " (" + << Target.symbolName() << ")\n"; + if (Format != MachO::DYLD_CHAINED_IMPORT) + outs() << " addend = " << (int64_t)Target.addend() << '\n'; +} + +static void PrintChainedFixups(MachOObjectFile *O) { + // MachOObjectFile::getChainedFixupsHeader() reads LC_DYLD_CHAINED_FIXUPS. + // FIXME: Support chained fixups in __TEXT,__chain_starts section too. + auto ChainedFixupHeader = + unwrapOrError(O->getChainedFixupsHeader(), O->getFileName()); + if (!ChainedFixupHeader) + return; + + PrintChainedFixupsHeader(*ChainedFixupHeader); + + auto [SegCount, Segments] = + unwrapOrError(O->getChainedFixupsSegments(), O->getFileName()); + + auto SegNames = GetSegmentNames(O); + + size_t StartsIdx = 0; + outs() << "chained starts in image\n"; + outs() << " seg_count = " << SegCount << '\n'; + for (size_t I = 0; I < SegCount; ++I) { + uint64_t SegOffset = 0; + if (StartsIdx < Segments.size() && I == Segments[StartsIdx].SegIdx) { + SegOffset = Segments[StartsIdx].Offset; + ++StartsIdx; + } + + outs() << " seg_offset[" << I << "] = " << SegOffset << " (" + << SegNames[I] << ")\n"; + } + + for (const ChainedFixupsSegment &S : Segments) + PrintChainedFixupsSegment(S, SegNames[S.SegIdx]); + + auto FixupTargets = + unwrapOrError(O->getDyldChainedFixupTargets(), O->getFileName()); + + uint32_t ImportsFormat = ChainedFixupHeader->imports_format; + for (auto [Idx, Target] : enumerate(FixupTargets)) + PrintChainedFixupTarget(Target, Idx, ImportsFormat, O); } static void PrintDyldInfo(MachOObjectFile *O) { - outs() << "dyld information:" << '\n'; - printMachOChainedFixups(O); + Error Err = Error::success(); + + size_t SegmentWidth = strlen("segment"); + size_t SectionWidth = strlen("section"); + size_t AddressWidth = strlen("address"); + size_t AddendWidth = strlen("addend"); + size_t DylibWidth = strlen("dylib"); + const size_t PointerWidth = 2 + O->getBytesInAddress() * 2; + + auto HexLength = [](uint64_t Num) { + return Num ? (size_t)divideCeil(Log2_64(Num), 4) : 1; + }; + for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) { + SegmentWidth = std::max(SegmentWidth, Entry.segmentName().size()); + SectionWidth = std::max(SectionWidth, Entry.sectionName().size()); + AddressWidth = std::max(AddressWidth, HexLength(Entry.address()) + 2); + if (Entry.isBind()) { + AddendWidth = std::max(AddendWidth, HexLength(Entry.addend()) + 2); + DylibWidth = std::max(DylibWidth, Entry.symbolName().size()); + } + } + // Errors will be handled when printing the table. + if (Err) + consumeError(std::move(Err)); + + outs() << "dyld information:\n"; + outs() << left_justify("segment", SegmentWidth) << ' ' + << left_justify("section", SectionWidth) << ' ' + << left_justify("address", AddressWidth) << ' ' + << left_justify("pointer", PointerWidth) << " type " + << left_justify("addend", AddendWidth) << ' ' + << left_justify("dylib", DylibWidth) << " symbol/vm address\n"; + for (const object::MachOChainedFixupEntry &Entry : O->fixupTable(Err)) { + outs() << left_justify(Entry.segmentName(), SegmentWidth) << ' ' + << left_justify(Entry.sectionName(), SectionWidth) << ' ' << "0x" + << left_justify(utohexstr(Entry.address()), AddressWidth - 2) << ' ' + << format_hex(Entry.rawValue(), PointerWidth, true) << ' '; + if (Entry.isBind()) { + outs() << "bind " + << "0x" << left_justify(utohexstr(Entry.addend()), AddendWidth - 2) + << ' ' << left_justify(ordinalName(O, Entry.ordinal()), DylibWidth) + << ' ' << Entry.symbolName(); + if (Entry.flags() & MachO::BIND_SYMBOL_FLAGS_WEAK_IMPORT) + outs() << " (weak import)"; + outs() << '\n'; + } else { + assert(Entry.isRebase()); + outs() << "rebase"; + outs().indent(AddendWidth + DylibWidth + 2); + outs() << format("0x%" PRIX64, Entry.pointerValue()) << '\n'; + } + } + if (Err) + reportError(std::move(Err), O->getFileName()); + + // TODO: Print opcode-based fixups if the object uses those. } static void PrintDylibs(MachOObjectFile *O, bool JustId) { @@ -1916,8 +2147,9 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // UniversalHeaders or ArchiveHeaders. if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || - DataInCode || FunctionStarts || LinkOptHints || DyldInfo || DylibsUsed || - DylibId || Rpaths || ObjcMetaData || (!FilterSections.empty())) { + DataInCode || FunctionStartsType != FunctionStartsMode::None || + LinkOptHints || ChainedFixups || DyldInfo || DylibsUsed || DylibId || + Rpaths || ObjcMetaData || (!FilterSections.empty())) { if (LeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) @@ -1972,7 +2204,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, PrintIndirectSymbols(MachOOF, Verbose); if (DataInCode) PrintDataInCodeTable(MachOOF, Verbose); - if (FunctionStarts) + if (FunctionStartsType != FunctionStartsMode::None) PrintFunctionStarts(MachOOF); if (LinkOptHints) PrintLinkOptHints(MachOOF); @@ -1988,6 +2220,8 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, DumpInfoPlistSectionContents(FileName, MachOOF); if (DyldInfo) PrintDyldInfo(MachOOF); + if (ChainedFixups) + PrintChainedFixups(MachOOF); if (DylibsUsed) PrintDylibs(MachOOF, false); if (DylibId) @@ -7189,6 +7423,108 @@ static void emitComments(raw_svector_ostream &CommentStream, CommentsToEmit.clear(); } +const MachOObjectFile * +objdump::getMachODSymObject(const MachOObjectFile *MachOOF, StringRef Filename, + std::unique_ptr<Binary> &DSYMBinary, + std::unique_ptr<MemoryBuffer> &DSYMBuf) { + const MachOObjectFile *DbgObj = MachOOF; + std::string DSYMPath; + + // Auto-detect w/o --dsym. + if (DSYMFile.empty()) { + sys::fs::file_status DSYMStatus; + Twine FilenameDSYM = Filename + ".dSYM"; + if (!status(FilenameDSYM, DSYMStatus)) { + if (sys::fs::is_directory(DSYMStatus)) { + SmallString<1024> Path; + FilenameDSYM.toVector(Path); + sys::path::append(Path, "Contents", "Resources", "DWARF", + sys::path::filename(Filename)); + DSYMPath = std::string(Path); + } else if (sys::fs::is_regular_file(DSYMStatus)) { + DSYMPath = FilenameDSYM.str(); + } + } + } + + if (DSYMPath.empty() && !DSYMFile.empty()) { + // If DSYMPath is a .dSYM directory, append the Mach-O file. + if (sys::fs::is_directory(DSYMFile) && + sys::path::extension(DSYMFile) == ".dSYM") { + SmallString<128> ShortName(sys::path::filename(DSYMFile)); + sys::path::replace_extension(ShortName, ""); + SmallString<1024> FullPath(DSYMFile); + sys::path::append(FullPath, "Contents", "Resources", "DWARF", ShortName); + DSYMPath = FullPath.str(); + } else { + DSYMPath = DSYMFile; + } + } + + if (!DSYMPath.empty()) { + // Load the file. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFileOrSTDIN(DSYMPath); + if (std::error_code EC = BufOrErr.getError()) { + reportError(errorCodeToError(EC), DSYMPath); + return nullptr; + } + + // We need to keep the file alive, because we're replacing DbgObj with it. + DSYMBuf = std::move(BufOrErr.get()); + + Expected<std::unique_ptr<Binary>> BinaryOrErr = + createBinary(DSYMBuf.get()->getMemBufferRef()); + if (!BinaryOrErr) { + reportError(BinaryOrErr.takeError(), DSYMPath); + return nullptr; + } + + // We need to keep the Binary alive with the buffer + DSYMBinary = std::move(BinaryOrErr.get()); + if (ObjectFile *O = dyn_cast<ObjectFile>(DSYMBinary.get())) { + // this is a Mach-O object file, use it + if (MachOObjectFile *MachDSYM = dyn_cast<MachOObjectFile>(&*O)) { + DbgObj = MachDSYM; + } else { + WithColor::error(errs(), "llvm-objdump") + << DSYMPath << " is not a Mach-O file type.\n"; + return nullptr; + } + } else if (auto *UB = dyn_cast<MachOUniversalBinary>(DSYMBinary.get())) { + // this is a Universal Binary, find a Mach-O for this architecture + uint32_t CPUType, CPUSubType; + const char *ArchFlag; + if (MachOOF->is64Bit()) { + const MachO::mach_header_64 H_64 = MachOOF->getHeader64(); + CPUType = H_64.cputype; + CPUSubType = H_64.cpusubtype; + } else { + const MachO::mach_header H = MachOOF->getHeader(); + CPUType = H.cputype; + CPUSubType = H.cpusubtype; + } + Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr, + &ArchFlag); + Expected<std::unique_ptr<MachOObjectFile>> MachDSYM = + UB->getMachOObjectForArch(ArchFlag); + if (!MachDSYM) { + reportError(MachDSYM.takeError(), DSYMPath); + return nullptr; + } + + // We need to keep the Binary alive with the buffer + DbgObj = &*MachDSYM.get(); + DSYMBinary = std::move(*MachDSYM); + } else { + WithColor::error(errs(), "llvm-objdump") + << DSYMPath << " is not a Mach-O or Universal file type.\n"; + return nullptr; + } + } + return DbgObj; +} + static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef DisSegName, StringRef DisSectName) { const char *McpuDefault = nullptr; @@ -7363,90 +7699,15 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, std::unique_ptr<Binary> DSYMBinary; std::unique_ptr<MemoryBuffer> DSYMBuf; if (UseDbg) { - ObjectFile *DbgObj = MachOOF; - - // A separate DSym file path was specified, parse it as a macho file, + // If separate DSym file path was specified, parse it as a macho file, // get the sections and supply it to the section name parsing machinery. - if (!DSYMFile.empty()) { - std::string DSYMPath(DSYMFile); - - // If DSYMPath is a .dSYM directory, append the Mach-O file. - if (llvm::sys::fs::is_directory(DSYMPath) && - llvm::sys::path::extension(DSYMPath) == ".dSYM") { - SmallString<128> ShortName(llvm::sys::path::filename(DSYMPath)); - llvm::sys::path::replace_extension(ShortName, ""); - SmallString<1024> FullPath(DSYMPath); - llvm::sys::path::append(FullPath, "Contents", "Resources", "DWARF", - ShortName); - DSYMPath = std::string(FullPath.str()); - } - - // Load the file. - ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = - MemoryBuffer::getFileOrSTDIN(DSYMPath); - if (std::error_code EC = BufOrErr.getError()) { - reportError(errorCodeToError(EC), DSYMPath); - return; - } - - // We need to keep the file alive, because we're replacing DbgObj with it. - DSYMBuf = std::move(BufOrErr.get()); - - Expected<std::unique_ptr<Binary>> BinaryOrErr = - createBinary(DSYMBuf.get()->getMemBufferRef()); - if (!BinaryOrErr) { - reportError(BinaryOrErr.takeError(), DSYMPath); - return; - } - - // We need to keep the Binary alive with the buffer - DSYMBinary = std::move(BinaryOrErr.get()); - if (ObjectFile *O = dyn_cast<ObjectFile>(DSYMBinary.get())) { - // this is a Mach-O object file, use it - if (MachOObjectFile *MachDSYM = dyn_cast<MachOObjectFile>(&*O)) { - DbgObj = MachDSYM; - } - else { - WithColor::error(errs(), "llvm-objdump") - << DSYMPath << " is not a Mach-O file type.\n"; - return; - } - } - else if (auto UB = dyn_cast<MachOUniversalBinary>(DSYMBinary.get())){ - // this is a Universal Binary, find a Mach-O for this architecture - uint32_t CPUType, CPUSubType; - const char *ArchFlag; - if (MachOOF->is64Bit()) { - const MachO::mach_header_64 H_64 = MachOOF->getHeader64(); - CPUType = H_64.cputype; - CPUSubType = H_64.cpusubtype; - } else { - const MachO::mach_header H = MachOOF->getHeader(); - CPUType = H.cputype; - CPUSubType = H.cpusubtype; - } - Triple T = MachOObjectFile::getArchTriple(CPUType, CPUSubType, nullptr, - &ArchFlag); - Expected<std::unique_ptr<MachOObjectFile>> MachDSYM = - UB->getMachOObjectForArch(ArchFlag); - if (!MachDSYM) { - reportError(MachDSYM.takeError(), DSYMPath); - return; - } - - // We need to keep the Binary alive with the buffer - DbgObj = &*MachDSYM.get(); - DSYMBinary = std::move(*MachDSYM); - } - else { - WithColor::error(errs(), "llvm-objdump") - << DSYMPath << " is not a Mach-O or Universal file type.\n"; - return; - } + if (const ObjectFile *DbgObj = + getMachODSymObject(MachOOF, Filename, DSYMBinary, DSYMBuf)) { + // Setup the DIContext + diContext = DWARFContext::create(*DbgObj); + } else { + return; } - - // Setup the DIContext - diContext = DWARFContext::create(*DbgObj); } if (FilterSections.empty()) @@ -7655,7 +7916,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, Annotations); if (gotInst) { if (ShowRawInsn || Arch == Triple::arm) { - dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs()); + dumpBytes(ArrayRef(Bytes.data() + Index, Size), outs()); } formatted_raw_ostream FormattedOS(outs()); StringRef AnnotationsStr = Annotations.str(); @@ -7736,7 +7997,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } if (ShowRawInsn || Arch == Triple::arm) { outs() << "\t"; - dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); + dumpBytes(ArrayRef(Bytes.data() + Index, InstSize), outs()); } StringRef AnnotationsStr = Annotations.str(); IP->printInst(&Inst, PC, AnnotationsStr, *STI, outs()); @@ -8445,6 +8706,9 @@ static void PrintMachHeader(uint32_t magic, uint32_t cputype, case MachO::MH_KEXT_BUNDLE: outs() << " KEXTBUNDLE"; break; + case MachO::MH_FILESET: + outs() << " FILESET"; + break; default: outs() << format(" %10u", filetype); break; @@ -8657,6 +8921,12 @@ static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, outs() << " PROTECTED_VERSION_1"; flags &= ~MachO::SG_PROTECTED_VERSION_1; } + if (flags & MachO::SG_READ_ONLY) { + // Apple's otool prints the SG_ prefix for this flag, but not for the + // others. + outs() << " SG_READ_ONLY"; + flags &= ~MachO::SG_READ_ONLY; + } if (flags) outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n"; else @@ -8754,6 +9024,8 @@ static void PrintSection(const char *sectname, const char *segname, outs() << " S_THREAD_LOCAL_VARIABLE_POINTERS\n"; else if (section_type == MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS) outs() << " S_THREAD_LOCAL_INIT_FUNCTION_POINTERS\n"; + else if (section_type == MachO::S_INIT_FUNC_OFFSETS) + outs() << " S_INIT_FUNC_OFFSETS\n"; else outs() << format("0x%08" PRIx32, section_type) << "\n"; outs() << "attributes"; @@ -10381,6 +10653,8 @@ static StringRef ordinalName(const object::MachOObjectFile *Obj, int Ordinal) { return "main-executable"; case MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP: return "flat-namespace"; + case MachO::BIND_SPECIAL_DYLIB_WEAK_LOOKUP: + return "weak"; default: if (Ordinal > 0) { std::error_code EC = diff --git a/llvm/tools/llvm-objdump/MachODump.h b/llvm/tools/llvm-objdump/MachODump.h index 12783e15b425..d9d3a70663f6 100644 --- a/llvm/tools/llvm-objdump/MachODump.h +++ b/llvm/tools/llvm-objdump/MachODump.h @@ -16,12 +16,14 @@ namespace llvm { class Error; class StringRef; +class MemoryBuffer; namespace object { class MachOObjectFile; class MachOUniversalBinary; class ObjectFile; class RelocationRef; +class Binary; } // namespace object namespace opt { @@ -32,17 +34,20 @@ namespace objdump { void parseMachOOptions(const llvm::opt::InputArgList &InputArgs); +enum class FunctionStartsMode { Addrs, Names, Both, None }; + // MachO specific options extern bool Bind; extern bool DataInCode; extern std::string DisSymName; +extern bool ChainedFixups; extern bool DyldInfo; extern bool DylibId; extern bool DylibsUsed; extern bool ExportsTrie; extern bool FirstPrivateHeader; extern bool FullLeadingAddr; -extern bool FunctionStarts; +extern FunctionStartsMode FunctionStartsType; extern bool IndirectSymbols; extern bool InfoPlist; extern bool LazyBind; @@ -60,6 +65,11 @@ Error getMachORelocationValueString(const object::MachOObjectFile *Obj, const object::RelocationRef &RelRef, llvm::SmallVectorImpl<char> &Result); +const object::MachOObjectFile * +getMachODSymObject(const object::MachOObjectFile *O, StringRef Filename, + std::unique_ptr<object::Binary> &DSYMBinary, + std::unique_ptr<MemoryBuffer> &DSYMBuf); + void parseInputMachO(StringRef Filename); void parseInputMachO(object::MachOUniversalBinary *UB); diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index 00d7d8ccff17..de7f883d24a8 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -1,5 +1,10 @@ include "llvm/Option/OptParser.td" +multiclass B<string name, string help1, string help2> { + def NAME: Flag<["--"], name>, HelpText<help1>; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>; +} + multiclass Eq<string name, string help> { def NAME : Separate<["--"], name>; def NAME #_eq : Joined<["--"], name #"=">, @@ -33,12 +38,22 @@ def arch_name_EQ : Joined<["--"], "arch-name=">, def archive_headers : Flag<["--"], "archive-headers">, HelpText<"Display archive header information">; +defm build_id : + Eq<"build-id", "Build ID to look up. Once found, added as an input file">, + MetaVarName<"<hex>">; + def : Flag<["-"], "a">, Alias<archive_headers>, HelpText<"Alias for --archive-headers">; def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">; +defm debug_file_directory : + Eq<"debug-file-directory", "Path to directory where to look for debug files">, + MetaVarName<"<dir>">; + +defm debuginfod : B<"debuginfod", "Use debuginfod to find debug files", "Don't use debuginfod to find debug files">; + def disassemble : Flag<["--"], "disassemble">, HelpText<"Disassemble all executable sections found in the input files">; def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">; @@ -118,7 +133,9 @@ def no_show_raw_insn : Flag<["--"], "no-show-raw-insn">, "do not print the instruction bytes.">; def no_leading_addr : Flag<["--"], "no-leading-addr">, - HelpText<"When disassembling, do not print leading addresses">; + HelpText<"When disassembling, do not print leading addresses for instructions or inline relocations">; +def : Flag<["--"], "no-addresses">, Alias<no_leading_addr>, + HelpText<"Alias for --no-leading-addr">; def raw_clang_ast : Flag<["--"], "raw-clang-ast">, HelpText<"Dump the raw binary contents of the clang AST section">; @@ -153,6 +170,10 @@ def : Flag<["--"], "headers">, Alias<section_headers>, def : Flag<["-"], "h">, Alias<section_headers>, HelpText<"Alias for --section-headers">; +def show_all_symbols : Flag<["--"], "show-all-symbols">, + HelpText<"Show all symbols during disassembly, even if multiple " + "symbols are defined at the same location">; + def show_lma : Flag<["--"], "show-lma">, HelpText<"Display LMA column when dumping ELF section headers">; @@ -284,11 +305,15 @@ def data_in_code : Flag<["--"], "data-in-code">, HelpText<"Print the data in code table for Mach-O objects (requires --macho)">, Group<grp_mach_o>; -def function_starts : Flag<["--"], "function-starts">, - HelpText<"Print the function starts table for " - "Mach-O objects (requires --macho)">, +def function_starts_EQ : Joined<["--"], "function-starts=">, + HelpText<"Print the function starts table for Mach-O objects. " + "Options: addrs (default), names, both (requires --macho)">, + Values<"addrs,names,both">, Group<grp_mach_o>; +def : Flag<["--"], "function-starts">, Alias<function_starts_EQ>, + AliasArgs<["addrs"]>, Group<grp_mach_o>; + def link_opt_hints : Flag<["--"], "link-opt-hints">, HelpText<"Print the linker optimization hints for " "Mach-O objects (requires --macho)">, @@ -299,11 +324,15 @@ def info_plist : Flag<["--"], "info-plist">, "Mach-O objects (requires --macho)">, Group<grp_mach_o>; -def dyld_info : Flag<["--"], "dyld_info">, - HelpText<"Print bind and rebase information used by dyld to resolve " - "external references in a final linked binary " - "(requires --macho)">, - Group<grp_mach_o>; +def chained_fixups : Flag<["--"], "chained-fixups">, + HelpText<"Print chained fixup information (requires --macho)">, + Group<grp_mach_o>; + +def dyld_info : Flag<["--"], "dyld-info">, + HelpText<"Print bind and rebase information used by dyld to resolve " + "external references in a final linked binary " + "(requires --macho)">, + Group<grp_mach_o>; def dylibs_used : Flag<["--"], "dylibs-used">, HelpText<"Print the shared libraries used for linked " diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index 46334c249070..4ac6b99e79bb 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -10,6 +10,7 @@ /// This file implements the offloading-specific dumper for llvm-objdump. /// //===----------------------------------------------------------------------===// + #include "OffloadDump.h" #include "llvm-objdump.h" #include "llvm/Object/ELFObjectFile.h" @@ -46,67 +47,34 @@ static void printBinary(const OffloadBinary &OB, uint64_t Index) { << getOffloadKindName(OB.getOffloadKind()) << "\n"; } -static Error visitAllBinaries(const OffloadBinary &OB) { - uint64_t Offset = 0; - uint64_t Index = 0; - while (Offset < OB.getMemoryBufferRef().getBufferSize()) { - MemoryBufferRef Buffer = - MemoryBufferRef(OB.getData().drop_front(Offset), OB.getFileName()); - auto BinaryOrErr = OffloadBinary::create(Buffer); - if (!BinaryOrErr) - return BinaryOrErr.takeError(); - - OffloadBinary &Binary = **BinaryOrErr; - printBinary(Binary, Index++); - - Offset += Binary.getSize(); - } - return Error::success(); -} - /// Print the embedded offloading contents of an ObjectFile \p O. void llvm::dumpOffloadBinary(const ObjectFile &O) { - if (!O.isELF()) { - reportWarning("--offloading is currently only supported for ELF targets", - O.getFileName()); + if (!O.isELF() && !O.isCOFF()) { + reportWarning( + "--offloading is currently only supported for COFF and ELF targets", + O.getFileName()); return; } - for (ELFSectionRef Sec : O.sections()) { - if (Sec.getType() != ELF::SHT_LLVM_OFFLOADING) - continue; - - Expected<StringRef> Contents = Sec.getContents(); - if (!Contents) - reportError(Contents.takeError(), O.getFileName()); - - std::unique_ptr<MemoryBuffer> Buffer = - MemoryBuffer::getMemBuffer(*Contents, O.getFileName(), false); - if (!isAddrAligned(Align(OffloadBinary::getAlignment()), - Buffer->getBufferStart())) - Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(), - Buffer->getBufferIdentifier()); - auto BinaryOrErr = OffloadBinary::create(*Buffer); - if (!BinaryOrErr) - reportError(O.getFileName(), "while extracting offloading files: " + - toString(BinaryOrErr.takeError())); - OffloadBinary &Binary = **BinaryOrErr; + SmallVector<OffloadFile> Binaries; + if (Error Err = extractOffloadBinaries(O.getMemoryBufferRef(), Binaries)) + reportError(O.getFileName(), "while extracting offloading files: " + + toString(std::move(Err))); - // Print out all the binaries that are contained in this buffer. If we fail - // to parse a binary before reaching the end of the buffer emit a warning. - if (Error Err = visitAllBinaries(Binary)) - reportWarning("while parsing offloading files: " + - toString(std::move(Err)), - O.getFileName()); - } + // Print out all the binaries that are contained in this buffer. + for (uint64_t I = 0, E = Binaries.size(); I != E; ++I) + printBinary(*Binaries[I].getBinary(), I); } /// Print the contents of an offload binary file \p OB. This may contain /// multiple binaries stored in the same buffer. void llvm::dumpOffloadSections(const OffloadBinary &OB) { - // Print out all the binaries that are contained at this buffer. If we fail to - // parse a binary before reaching the end of the buffer emit a warning. - if (Error Err = visitAllBinaries(OB)) - reportWarning("while parsing offloading files: " + toString(std::move(Err)), - OB.getFileName()); + SmallVector<OffloadFile> Binaries; + if (Error Err = extractOffloadBinaries(OB.getMemoryBufferRef(), Binaries)) + reportError(OB.getFileName(), "while extracting offloading files: " + + toString(std::move(Err))); + + // Print out all the binaries that are contained in this buffer. + for (uint64_t I = 0, E = Binaries.size(); I != E; ++I) + printBinary(*Binaries[I].getBinary(), I); } diff --git a/llvm/tools/llvm-objdump/OtoolOpts.td b/llvm/tools/llvm-objdump/OtoolOpts.td index e8bef284c0e9..dc7a5b445cff 100644 --- a/llvm/tools/llvm-objdump/OtoolOpts.td +++ b/llvm/tools/llvm-objdump/OtoolOpts.td @@ -37,13 +37,16 @@ def V : Flag<["-"], "V">, def x : Flag<["-"], "x">, HelpText<"print all text sections">; def X : Flag<["-"], "X">, HelpText<"omit leading addresses or headers">; +def chained_fixups : Flag<["-"], "chained_fixups">, + HelpText<"print chained fixup information">; +def dyld_info : Flag<["-"], "dyld_info">, + HelpText<"print bind and rebase information">; + // Not (yet?) implemented: // def a : Flag<["-"], "a">, HelpText<"print archive header">; // -c print argument strings of a core file // -m don't use archive(member) syntax -// -dyld_info // -dyld_opcodes -// -chained_fixups // -addr_slide=arg // -function_offsets diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp index c8ea6b543245..6736cbc9ad5f 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -31,7 +31,7 @@ unsigned getInstStartColumn(const MCSubtargetInfo &STI) { } bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { - if (LocExpr.Range == None) + if (LocExpr.Range == std::nullopt) return false; return LocExpr.Range->SectionIndex == Addr.SectionIndex && LocExpr.Range->LowPC <= Addr.Address && @@ -42,7 +42,17 @@ void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const { DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, Unit->getContext().isLittleEndian(), 0); DWARFExpression Expression(Data, Unit->getAddressByteSize()); - Expression.printCompact(OS, MRI); + + auto GetRegName = [&MRI, &OS](uint64_t DwarfRegNum, bool IsEH) -> StringRef { + if (std::optional<unsigned> LLVMRegNum = + MRI.getLLVMRegNum(DwarfRegNum, IsEH)) + if (const char *RegName = MRI.getName(*LLVMRegNum)) + return StringRef(RegName); + OS << "<unknown register " << DwarfRegNum << ">"; + return {}; + }; + + Expression.printCompact(OS, GetRegName); } void LiveVariablePrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) { diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h index 29ef19c98c80..6209bb0e43e4 100644 --- a/llvm/tools/llvm-objdump/SourcePrinter.h +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -13,6 +13,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/FormattedStream.h" #include <unordered_map> diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp index dd1570e1736c..7171e2eb6eb3 100644 --- a/llvm/tools/llvm-objdump/XCOFFDump.cpp +++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp @@ -43,31 +43,31 @@ Error objdump::getXCOFFRelocationValueString(const XCOFFObjectFile &Obj, return Error::success(); } -Optional<XCOFF::StorageMappingClass> +std::optional<XCOFF::StorageMappingClass> objdump::getXCOFFSymbolCsectSMC(const XCOFFObjectFile &Obj, const SymbolRef &Sym) { const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); if (!SymRef.isCsectSymbol()) - return None; + return std::nullopt; auto CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); if (!CsectAuxEntOrErr) - return None; + return std::nullopt; return CsectAuxEntOrErr.get().getStorageMappingClass(); } -Optional<object::SymbolRef> +std::optional<object::SymbolRef> objdump::getXCOFFSymbolContainingSymbolRef(const XCOFFObjectFile &Obj, const SymbolRef &Sym) { const XCOFFSymbolRef SymRef = Obj.toSymbolRef(Sym.getRawDataRefImpl()); if (!SymRef.isCsectSymbol()) - return None; + return std::nullopt; Expected<XCOFFCsectAuxRef> CsectAuxEntOrErr = SymRef.getXCOFFCsectAuxRef(); if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel()) - return None; + return std::nullopt; uint32_t Idx = static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength()); DataRefImpl DRI; @@ -94,9 +94,9 @@ std::string objdump::getXCOFFSymbolDescription(const SymbolInfoTy &SymbolInfo, std::string Result; // Dummy symbols have no symbol index. if (SymbolInfo.XCOFFSymInfo.Index) - Result = ("(idx: " + Twine(SymbolInfo.XCOFFSymInfo.Index.value()) + ") " + - SymbolName) - .str(); + Result = + ("(idx: " + Twine(*SymbolInfo.XCOFFSymInfo.Index) + ") " + SymbolName) + .str(); else Result.append(SymbolName.begin(), SymbolName.end()); diff --git a/llvm/tools/llvm-objdump/XCOFFDump.h b/llvm/tools/llvm-objdump/XCOFFDump.h index 461605940946..35d1c0f1ebbe 100644 --- a/llvm/tools/llvm-objdump/XCOFFDump.h +++ b/llvm/tools/llvm-objdump/XCOFFDump.h @@ -16,11 +16,11 @@ namespace llvm { struct SymbolInfoTy; namespace objdump { -Optional<XCOFF::StorageMappingClass> +std::optional<XCOFF::StorageMappingClass> getXCOFFSymbolCsectSMC(const object::XCOFFObjectFile &Obj, const object::SymbolRef &Sym); -Optional<object::SymbolRef> +std::optional<object::SymbolRef> getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj, const object::SymbolRef &Sym); diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index fd83dc197fe9..930b132533cd 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -25,7 +25,6 @@ #include "WasmDump.h" #include "XCOFFDump.h" #include "llvm/ADT/IndexedMap.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SmallSet.h" @@ -36,6 +35,9 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Debuginfod/BuildIDFetcher.h" +#include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/Debuginfod/HTTPClient.h" #include "llvm/Demangle/Demangle.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -51,6 +53,7 @@ #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/BuildID.h" #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" @@ -82,6 +85,7 @@ #include <algorithm> #include <cctype> #include <cstring> +#include <optional> #include <system_error> #include <unordered_map> #include <utility> @@ -93,18 +97,19 @@ using namespace llvm::opt; namespace { -class CommonOptTable : public opt::OptTable { +class CommonOptTable : public opt::GenericOptTable { public: CommonOptTable(ArrayRef<Info> OptionInfos, const char *Usage, const char *Description) - : OptTable(OptionInfos), Usage(Usage), Description(Description) { + : opt::GenericOptTable(OptionInfos), Usage(Usage), + Description(Description) { setGroupedShortOptions(true); } void printHelp(StringRef Argv0, bool ShowHidden = false) const { Argv0 = sys::path::filename(Argv0); - opt::OptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), Description, - ShowHidden, ShowHidden); + opt::GenericOptTable::printHelp(outs(), (Argv0 + Usage).str().c_str(), + Description, ShowHidden, ShowHidden); // TODO Replace this with OptTable API once it adds extrahelp support. outs() << "\nPass @FILE as argument to read options from FILE.\n"; } @@ -115,28 +120,31 @@ private: }; // ObjdumpOptID is in ObjdumpOptID.h - -#define PREFIX(NAME, VALUE) const char *const OBJDUMP_##NAME[] = VALUE; +namespace objdump_opt { +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ + std::size(NAME##_init) - 1); #include "ObjdumpOpts.inc" #undef PREFIX static constexpr opt::OptTable::Info ObjdumpInfoTable[] = { -#define OBJDUMP_nullptr nullptr #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ - {OBJDUMP_##PREFIX, NAME, HELPTEXT, \ - METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OBJDUMP_##GROUP, \ - OBJDUMP_##ALIAS, ALIASARGS, VALUES}, + {PREFIX, NAME, HELPTEXT, \ + METAVAR, OBJDUMP_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OBJDUMP_##GROUP, \ + OBJDUMP_##ALIAS, ALIASARGS, VALUES}, #include "ObjdumpOpts.inc" #undef OPTION -#undef OBJDUMP_nullptr }; +} // namespace objdump_opt class ObjdumpOptTable : public CommonOptTable { public: ObjdumpOptTable() - : CommonOptTable(ObjdumpInfoTable, " [options] <input object files>", + : CommonOptTable(objdump_opt::ObjdumpInfoTable, + " [options] <input object files>", "llvm object file dumper") {} }; @@ -149,27 +157,30 @@ enum OtoolOptID { #undef OPTION }; -#define PREFIX(NAME, VALUE) const char *const OTOOL_##NAME[] = VALUE; +namespace otool { +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ + std::size(NAME##_init) - 1); #include "OtoolOpts.inc" #undef PREFIX static constexpr opt::OptTable::Info OtoolInfoTable[] = { -#define OTOOL_nullptr nullptr #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ - {OTOOL_##PREFIX, NAME, HELPTEXT, \ - METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \ - PARAM, FLAGS, OTOOL_##GROUP, \ - OTOOL_##ALIAS, ALIASARGS, VALUES}, + {PREFIX, NAME, HELPTEXT, \ + METAVAR, OTOOL_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OTOOL_##GROUP, \ + OTOOL_##ALIAS, ALIASARGS, VALUES}, #include "OtoolOpts.inc" #undef OPTION -#undef OTOOL_nullptr }; +} // namespace otool class OtoolOptTable : public CommonOptTable { public: OtoolOptTable() - : CommonOptTable(OtoolInfoTable, " [option...] [file...]", + : CommonOptTable(otool::OtoolInfoTable, " [option...] [file...]", "Mach-O object file displaying tool") {} }; @@ -207,6 +218,7 @@ bool objdump::PrintImmHex; bool objdump::PrivateHeaders; std::vector<std::string> objdump::FilterSections; bool objdump::SectionHeaders; +static bool ShowAllSymbols; static bool ShowLMA; bool objdump::PrintSource; @@ -232,6 +244,9 @@ static StringSet<> DisasmSymbolSet; StringSet<> objdump::FoundSectionSet; static StringRef ToolName; +std::unique_ptr<BuildIDFetcher> BIDFetcher; +ExitOnError ExitOnErr; + namespace { struct FilterResult { // True if the section should not be skipped. @@ -454,16 +469,24 @@ static bool hasMappingSymbols(const ObjectFile &Obj) { return isArmElf(Obj) || isAArch64Elf(Obj) || isCSKYElf(Obj) ; } +static bool isMappingSymbol(const SymbolInfoTy &Sym) { + return Sym.Name.startswith("$d") || Sym.Name.startswith("$x") || + Sym.Name.startswith("$a") || Sym.Name.startswith("$t"); +} + static void printRelocation(formatted_raw_ostream &OS, StringRef FileName, const RelocationRef &Rel, uint64_t Address, bool Is64Bits) { - StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": "; + StringRef Fmt = Is64Bits ? "%016" PRIx64 ": " : "%08" PRIx64 ": "; SmallString<16> Name; SmallString<32> Val; Rel.getTypeName(Name); if (Error E = getRelocationValueString(Rel, Val)) reportError(std::move(E), FileName); - OS << format(Fmt.data(), Address) << Name << "\t" << Val; + OS << (Is64Bits || !LeadingAddr ? "\t\t" : "\t\t\t"); + if (LeadingAddr) + OS << format(Fmt.data(), Address); + OS << Name << "\t" << Val; } static void AlignToInstStartColumn(size_t Start, const MCSubtargetInfo &STI, @@ -631,10 +654,10 @@ public: if (Bytes.size() >= 4) { // D should be casted to uint32_t here as it is passed by format to // snprintf as vararg. - for (uint32_t D : makeArrayRef( - reinterpret_cast<const support::little32_t *>(Bytes.data()), - Bytes.size() / 4)) - OS << format(" %08" PRIX32, D); + for (uint32_t D : + ArrayRef(reinterpret_cast<const support::little32_t *>(Bytes.data()), + Bytes.size() / 4)) + OS << format(" %08" PRIX32, D); } else { for (unsigned char B : Bytes) OS << format(" %02" PRIX8, B); @@ -690,14 +713,14 @@ public: OS << ' ' << format_hex_no_prefix( llvm::support::endian::read<uint16_t>( - Bytes.data() + Pos, llvm::support::little), + Bytes.data() + Pos, InstructionEndianness), 4); } else { for (; Pos + 4 <= End; Pos += 4) OS << ' ' << format_hex_no_prefix( llvm::support::endian::read<uint32_t>( - Bytes.data() + Pos, llvm::support::little), + Bytes.data() + Pos, InstructionEndianness), 8); } if (Pos < End) { @@ -713,6 +736,13 @@ public: } else OS << "\t<unknown>"; } + + void setInstructionEndianness(llvm::support::endianness Endianness) { + InstructionEndianness = Endianness; + } + +private: + llvm::support::endianness InstructionEndianness = llvm::support::little; }; ARMPrettyPrinter ARMPrettyPrinterInst; @@ -844,19 +874,19 @@ addDynamicElfSymbols(const ELFObjectFileBase &Obj, llvm_unreachable("Unsupported binary format"); } -static Optional<SectionRef> getWasmCodeSection(const WasmObjectFile &Obj) { +static std::optional<SectionRef> getWasmCodeSection(const WasmObjectFile &Obj) { for (auto SecI : Obj.sections()) { const WasmSection &Section = Obj.getWasmSection(SecI); if (Section.Type == wasm::WASM_SEC_CODE) return SecI; } - return None; + return std::nullopt; } static void addMissingWasmCodeSymbols(const WasmObjectFile &Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols) { - Optional<SectionRef> Section = getWasmCodeSection(Obj); + std::optional<SectionRef> Section = getWasmCodeSection(Obj); if (!Section) return; SectionSymbolsTy &Symbols = AllSymbols[*Section]; @@ -884,7 +914,7 @@ addMissingWasmCodeSymbols(const WasmObjectFile &Obj, static void addPltEntries(const ObjectFile &Obj, std::map<SectionRef, SectionSymbolsTy> &AllSymbols, StringSaver &Saver) { - Optional<SectionRef> Plt = None; + std::optional<SectionRef> Plt; for (const SectionRef &Section : Obj.sections()) { Expected<StringRef> SecNameOrErr = Section.getName(); if (!SecNameOrErr) { @@ -1065,7 +1095,7 @@ SymbolInfoTy objdump::createSymbolInfo(const ObjectFile &Obj, DataRefImpl SymbolDRI = Symbol.getRawDataRefImpl(); const uint32_t SymbolIndex = XCOFFObj.getSymbolIndex(SymbolDRI.p); - Optional<XCOFF::StorageMappingClass> Smc = + std::optional<XCOFF::StorageMappingClass> Smc = getXCOFFSymbolCsectSMC(XCOFFObj, Symbol); return SymbolInfoTy(Addr, Name, Smc, SymbolIndex, isLabel(XCOFFObj, Symbol)); @@ -1082,7 +1112,7 @@ static SymbolInfoTy createDummySymbolInfo(const ObjectFile &Obj, const uint64_t Addr, StringRef &Name, uint8_t Type) { if (Obj.isXCOFF() && SymbolDescription) - return SymbolInfoTy(Addr, Name, None, None, false); + return SymbolInfoTy(Addr, Name, std::nullopt, std::nullopt, false); else return SymbolInfoTy(Addr, Name, Type); } @@ -1172,8 +1202,9 @@ static void addSymbolizer( for (size_t Index = 0; Index != Bytes.size();) { MCInst Inst; uint64_t Size; - ArrayRef<uint8_t> ThisBytes = Bytes.slice(Index - SectionAddr); - DisAsm->getInstruction(Inst, Size, ThisBytes, Index, nulls()); + ArrayRef<uint8_t> ThisBytes = Bytes.slice(Index); + const uint64_t ThisAddr = SectionAddr + Index; + DisAsm->getInstruction(Inst, Size, ThisBytes, ThisAddr, nulls()); if (Size == 0) Size = std::min<uint64_t>(ThisBytes.size(), DisAsm->suggestBytesToSkip(ThisBytes, Index)); @@ -1250,8 +1281,27 @@ static void createFakeELFSections(ObjectFile &Obj) { llvm_unreachable("Unsupported binary format"); } +// Tries to fetch a more complete version of the given object file using its +// Build ID. Returns std::nullopt if nothing was found. +static std::optional<OwningBinary<Binary>> +fetchBinaryByBuildID(const ObjectFile &Obj) { + std::optional<object::BuildIDRef> BuildID = getBuildID(&Obj); + if (!BuildID) + return std::nullopt; + std::optional<std::string> Path = BIDFetcher->fetch(*BuildID); + if (!Path) + return std::nullopt; + Expected<OwningBinary<Binary>> DebugBinary = createBinary(*Path); + if (!DebugBinary) { + reportWarning(toString(DebugBinary.takeError()), *Path); + return std::nullopt; + } + return std::move(*DebugBinary); +} + static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, - MCContext &Ctx, MCDisassembler *PrimaryDisAsm, + const ObjectFile &DbgObj, MCContext &Ctx, + MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, const MCInstrAnalysis *MIA, MCInstPrinter *IP, const MCSubtargetInfo *PrimarySTI, @@ -1376,7 +1426,7 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, LiveVariablePrinter LVP(*Ctx.getRegisterInfo(), *STI); if (DbgVariables != DVDisabled) { - DICtx = DWARFContext::create(Obj); + DICtx = DWARFContext::create(DbgObj); for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units()) LVP.addCompileUnit(CU->getUnitDIE(false)); } @@ -1384,13 +1434,13 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, LLVM_DEBUG(LVP.dump()); std::unordered_map<uint64_t, BBAddrMap> AddrToBBAddrMap; - auto ReadBBAddrMap = [&](Optional<unsigned> SectionIndex = None) { + auto ReadBBAddrMap = [&](std::optional<unsigned> SectionIndex = + std::nullopt) { AddrToBBAddrMap.clear(); if (const auto *Elf = dyn_cast<ELFObjectFileBase>(&Obj)) { auto BBAddrMapsOrErr = Elf->readBBAddrMap(SectionIndex); if (!BBAddrMapsOrErr) - reportWarning(toString(BBAddrMapsOrErr.takeError()), - Obj.getFileName()); + reportWarning(toString(BBAddrMapsOrErr.takeError()), Obj.getFileName()); for (auto &FunctionBBAddrMap : *BBAddrMapsOrErr) AddrToBBAddrMap.emplace(FunctionBBAddrMap.Addr, std::move(FunctionBBAddrMap)); @@ -1474,28 +1524,118 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, std::vector<RelocationRef> Rels = RelocMap[Section]; std::vector<RelocationRef>::const_iterator RelCur = Rels.begin(); std::vector<RelocationRef>::const_iterator RelEnd = Rels.end(); - // Disassemble symbol by symbol. - for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) { - std::string SymbolName = Symbols[SI].Name.str(); - if (Demangle) - SymbolName = demangle(SymbolName); - - // Skip if --disassemble-symbols is not empty and the symbol is not in - // the list. - if (!DisasmSymbolSet.empty() && !DisasmSymbolSet.count(SymbolName)) - continue; + // Loop over each chunk of code between two points where at least + // one symbol is defined. + for (size_t SI = 0, SE = Symbols.size(); SI != SE;) { + // Advance SI past all the symbols starting at the same address, + // and make an ArrayRef of them. + unsigned FirstSI = SI; uint64_t Start = Symbols[SI].Addr; + ArrayRef<SymbolInfoTy> SymbolsHere; + while (SI != SE && Symbols[SI].Addr == Start) + ++SI; + SymbolsHere = ArrayRef<SymbolInfoTy>(&Symbols[FirstSI], SI - FirstSI); + + // Get the demangled names of all those symbols. We end up with a vector + // of StringRef that holds the names we're going to use, and a vector of + // std::string that stores the new strings returned by demangle(), if + // any. If we don't call demangle() then that vector can stay empty. + std::vector<StringRef> SymNamesHere; + std::vector<std::string> DemangledSymNamesHere; + if (Demangle) { + // Fetch the demangled names and store them locally. + for (const SymbolInfoTy &Symbol : SymbolsHere) + DemangledSymNamesHere.push_back(demangle(Symbol.Name.str())); + // Now we've finished modifying that vector, it's safe to make + // a vector of StringRefs pointing into it. + SymNamesHere.insert(SymNamesHere.begin(), DemangledSymNamesHere.begin(), + DemangledSymNamesHere.end()); + } else { + for (const SymbolInfoTy &Symbol : SymbolsHere) + SymNamesHere.push_back(Symbol.Name); + } + + // Distinguish ELF data from code symbols, which will be used later on to + // decide whether to 'disassemble' this chunk as a data declaration via + // dumpELFData(), or whether to treat it as code. + // + // If data _and_ code symbols are defined at the same address, the code + // takes priority, on the grounds that disassembling code is our main + // purpose here, and it would be a worse failure to _not_ interpret + // something that _was_ meaningful as code than vice versa. + // + // Any ELF symbol type that is not clearly data will be regarded as code. + // In particular, one of the uses of STT_NOTYPE is for branch targets + // inside functions, for which STT_FUNC would be inaccurate. + // + // So here, we spot whether there's any non-data symbol present at all, + // and only set the DisassembleAsData flag if there isn't. Also, we use + // this distinction to inform the decision of which symbol to print at + // the head of the section, so that if we're printing code, we print a + // code-related symbol name to go with it. + bool DisassembleAsData = false; + size_t DisplaySymIndex = SymbolsHere.size() - 1; + if (Obj.isELF() && !DisassembleAll && Section.isText()) { + DisassembleAsData = true; // unless we find a code symbol below + + for (size_t i = 0; i < SymbolsHere.size(); ++i) { + uint8_t SymTy = SymbolsHere[i].Type; + if (SymTy != ELF::STT_OBJECT && SymTy != ELF::STT_COMMON) { + DisassembleAsData = false; + DisplaySymIndex = i; + } + } + } + + // Decide which symbol(s) from this collection we're going to print. + std::vector<bool> SymsToPrint(SymbolsHere.size(), false); + // If the user has given the --disassemble-symbols option, then we must + // display every symbol in that set, and no others. + if (!DisasmSymbolSet.empty()) { + bool FoundAny = false; + for (size_t i = 0; i < SymbolsHere.size(); ++i) { + if (DisasmSymbolSet.count(SymNamesHere[i])) { + SymsToPrint[i] = true; + FoundAny = true; + } + } + + // And if none of the symbols here is one that the user asked for, skip + // disassembling this entire chunk of code. + if (!FoundAny) + continue; + } else { + // Otherwise, print whichever symbol at this location is last in the + // Symbols array, because that array is pre-sorted in a way intended to + // correlate with priority of which symbol to display. + SymsToPrint[DisplaySymIndex] = true; + } + + // Now that we know we're disassembling this section, override the choice + // of which symbols to display by printing _all_ of them at this address + // if the user asked for all symbols. + // + // That way, '--show-all-symbols --disassemble-symbol=foo' will print + // only the chunk of code headed by 'foo', but also show any other + // symbols defined at that address, such as aliases for 'foo', or the ARM + // mapping symbol preceding its code. + if (ShowAllSymbols) { + for (size_t i = 0; i < SymbolsHere.size(); ++i) + SymsToPrint[i] = true; + } + if (Start < SectionAddr || StopAddress <= Start) continue; - else - FoundDisasmSymbolSet.insert(SymbolName); + + for (size_t i = 0; i < SymbolsHere.size(); ++i) + FoundDisasmSymbolSet.insert(SymNamesHere[i]); // The end is the section end, the beginning of the next symbol, or // --stop-address. uint64_t End = std::min<uint64_t>(SectionAddr + SectSize, StopAddress); - if (SI + 1 < SE) - End = std::min(End, Symbols[SI + 1].Addr); + if (SI < SE) + End = std::min(End, Symbols[SI].Addr); if (Start >= End || End <= StartAddress) continue; Start -= SectionAddr; @@ -1510,13 +1650,22 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, } outs() << '\n'; - if (LeadingAddr) - outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", - SectionAddr + Start + VMAAdjustment); - if (Obj.isXCOFF() && SymbolDescription) { - outs() << getXCOFFSymbolDescription(Symbols[SI], SymbolName) << ":\n"; - } else - outs() << '<' << SymbolName << ">:\n"; + + for (size_t i = 0; i < SymbolsHere.size(); ++i) { + if (!SymsToPrint[i]) + continue; + + const SymbolInfoTy &Symbol = SymbolsHere[i]; + const StringRef SymbolName = SymNamesHere[i]; + + if (LeadingAddr) + outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ", + SectionAddr + Start + VMAAdjustment); + if (Obj.isXCOFF() && SymbolDescription) { + outs() << getXCOFFSymbolDescription(Symbol, SymbolName) << ":\n"; + } else + outs() << '<' << SymbolName << ">:\n"; + } // Don't print raw contents of a virtual section. A virtual section // doesn't have any contents in the file. @@ -1525,57 +1674,67 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, continue; } - auto Status = DisAsm->onSymbolStart(Symbols[SI], Size, - Bytes.slice(Start, End - Start), - SectionAddr + Start, CommentStream); - // To have round trippable disassembly, we fall back to decoding the - // remaining bytes as instructions. - // - // If there is a failure, we disassemble the failed region as bytes before - // falling back. The target is expected to print nothing in this case. - // - // If there is Success or SoftFail i.e no 'real' failure, we go ahead by - // Size bytes before falling back. - // So if the entire symbol is 'eaten' by the target: - // Start += Size // Now Start = End and we will never decode as - // // instructions - // - // Right now, most targets return None i.e ignore to treat a symbol - // separately. But WebAssembly decodes preludes for some symbols. + // See if any of the symbols defined at this location triggers target- + // specific disassembly behavior, e.g. of special descriptors or function + // prelude information. // - if (Status) { - if (Status.value() == MCDisassembler::Fail) { - outs() << "// Error in decoding " << SymbolName + // We stop this loop at the first symbol that triggers some kind of + // interesting behavior (if any), on the assumption that if two symbols + // defined at the same address trigger two conflicting symbol handlers, + // the object file is probably confused anyway, and it would make even + // less sense to present the output of _both_ handlers, because that + // would describe the same data twice. + for (size_t SHI = 0; SHI < SymbolsHere.size(); ++SHI) { + SymbolInfoTy Symbol = SymbolsHere[SHI]; + + auto Status = + DisAsm->onSymbolStart(Symbol, Size, Bytes.slice(Start, End - Start), + SectionAddr + Start, CommentStream); + + if (!Status) { + // If onSymbolStart returns std::nullopt, that means it didn't trigger + // any interesting handling for this symbol. Try the other symbols + // defined at this address. + continue; + } + + if (*Status == MCDisassembler::Fail) { + // If onSymbolStart returns Fail, that means it identified some kind + // of special data at this address, but wasn't able to disassemble it + // meaningfully. So we fall back to disassembling the failed region + // as bytes, assuming that the target detected the failure before + // printing anything. + // + // Return values Success or SoftFail (i.e no 'real' failure) are + // expected to mean that the target has emitted its own output. + // + // Either way, 'Size' will have been set to the amount of data + // covered by whatever prologue the target identified. So we advance + // our own position to beyond that. Sometimes that will be the entire + // distance to the next symbol, and sometimes it will be just a + // prologue and we should start disassembling instructions from where + // it left off. + outs() << "// Error in decoding " << SymNamesHere[SHI] << " : Decoding failed region as bytes.\n"; for (uint64_t I = 0; I < Size; ++I) { outs() << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true) << "\n"; } } - } else { - Size = 0; + Start += Size; + break; } - Start += Size; - Index = Start; if (SectionAddr < StartAddress) Index = std::max<uint64_t>(Index, StartAddress - SectionAddr); - // If there is a data/common symbol inside an ELF text section and we are - // only disassembling text (applicable all architectures), we are in a - // situation where we must print the data and not disassemble it. - if (Obj.isELF() && !DisassembleAll && Section.isText()) { - uint8_t SymTy = Symbols[SI].Type; - if (SymTy == ELF::STT_OBJECT || SymTy == ELF::STT_COMMON) { - dumpELFData(SectionAddr, Index, End, Bytes); - Index = End; - } + if (DisassembleAsData) { + dumpELFData(SectionAddr, Index, End, Bytes); + Index = End; + continue; } - bool CheckARMELFData = hasMappingSymbols(Obj) && - Symbols[SI].Type != ELF::STT_OBJECT && - !DisassembleAll; bool DumpARMELFData = false; formatted_raw_ostream FOS(outs()); @@ -1593,7 +1752,7 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, // same section. We rely on the markers introduced to understand what // we need to dump. If the data marker is within a function, it is // denoted as a word/short etc. - if (CheckARMELFData) { + if (!MappingSymbols.empty()) { char Kind = getMappingSymbolKind(MappingSymbols, Index); DumpARMELFData = Kind == 'd'; if (SecondarySTI) { @@ -1675,7 +1834,7 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, bool PrintTarget = MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target); if (!PrintTarget) - if (Optional<uint64_t> MaybeTarget = + if (std::optional<uint64_t> MaybeTarget = MIA->evaluateMemoryOperandAddress( Inst, STI, SectionAddr + Index, Size)) { Target = *MaybeTarget; @@ -1729,10 +1888,17 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, auto It = llvm::partition_point( *TargetSymbols, [=](const SymbolInfoTy &O) { return O.Addr <= Target; }); - if (It != TargetSymbols->begin()) { - TargetSym = &*(It - 1); - break; + while (It != TargetSymbols->begin()) { + --It; + // Skip mapping symbols to avoid possible ambiguity as they + // do not allow uniquely identifying the target address. + if (!hasMappingSymbols(Obj) || !isMappingSymbol(*It)) { + TargetSym = &*It; + break; + } } + if (TargetSym) + break; } // Print the labels corresponding to the target if there's any. @@ -1824,10 +1990,29 @@ static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, } static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) { + // If information useful for showing the disassembly is missing, try to find a + // more complete binary and disassemble that instead. + OwningBinary<Binary> FetchedBinary; + if (Obj->symbols().empty()) { + if (std::optional<OwningBinary<Binary>> FetchedBinaryOpt = + fetchBinaryByBuildID(*Obj)) { + if (auto *O = dyn_cast<ObjectFile>(FetchedBinaryOpt->getBinary())) { + if (!O->symbols().empty() || + (!O->sections().empty() && Obj->sections().empty())) { + FetchedBinary = std::move(*FetchedBinaryOpt); + Obj = O; + } + } + } + } + const Target *TheTarget = getTarget(Obj); // Package up features to be passed to target/subtarget - SubtargetFeatures Features = Obj->getFeatures(); + Expected<SubtargetFeatures> FeaturesValue = Obj->getFeatures(); + if (!FeaturesValue) + reportError(FeaturesValue.takeError(), Obj->getFileName()); + SubtargetFeatures Features = *FeaturesValue; if (!MAttrs.empty()) { for (unsigned I = 0; I != MAttrs.size(); ++I) Features.AddFeature(MAttrs[I]); @@ -1852,6 +2037,29 @@ static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) { if (MCPU.empty()) MCPU = Obj->tryGetCPUName().value_or("").str(); + if (isArmElf(*Obj)) { + // When disassembling big-endian Arm ELF, the instruction endianness is + // determined in a complex way. In relocatable objects, AAELF32 mandates + // that instruction endianness matches the ELF file endianness; in + // executable images, that's true unless the file header has the EF_ARM_BE8 + // flag, in which case instructions are little-endian regardless of data + // endianness. + // + // We must set the big-endian-instructions SubtargetFeature to make the + // disassembler read the instructions the right way round, and also tell + // our own prettyprinter to retrieve the encodings the same way to print in + // hex. + const auto *Elf32BE = dyn_cast<ELF32BEObjectFile>(Obj); + + if (Elf32BE && (Elf32BE->isRelocatableObject() || + !(Elf32BE->getPlatformFlags() & ELF::EF_ARM_BE8))) { + Features.AddFeature("+big-endian-instructions"); + ARMPrettyPrinterInst.setInstructionEndianness(llvm::support::big); + } else { + ARMPrettyPrinterInst.setInstructionEndianness(llvm::support::little); + } + } + std::unique_ptr<const MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString())); if (!STI) @@ -1903,16 +2111,42 @@ static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) { IP->setMCInstrAnalysis(MIA.get()); PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); - SourcePrinter SP(Obj, TheTarget->getName()); + + const ObjectFile *DbgObj = Obj; + if (!FetchedBinary.getBinary() && !Obj->hasDebugInfo()) { + if (std::optional<OwningBinary<Binary>> DebugBinaryOpt = + fetchBinaryByBuildID(*Obj)) { + if (auto *FetchedObj = + dyn_cast<const ObjectFile>(DebugBinaryOpt->getBinary())) { + if (FetchedObj->hasDebugInfo()) { + FetchedBinary = std::move(*DebugBinaryOpt); + DbgObj = FetchedObj; + } + } + } + } + + std::unique_ptr<object::Binary> DSYMBinary; + std::unique_ptr<MemoryBuffer> DSYMBuf; + if (!DbgObj->hasDebugInfo()) { + if (const MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*Obj)) { + DbgObj = objdump::getMachODSymObject(MachOOF, Obj->getFileName(), + DSYMBinary, DSYMBuf); + if (!DbgObj) + return; + } + } + + SourcePrinter SP(DbgObj, TheTarget->getName()); for (StringRef Opt : DisassemblerOptions) if (!IP->applyTargetSpecificCLOption(Opt)) reportError(Obj->getFileName(), "Unrecognized disassembler option: " + Opt); - disassembleObject(TheTarget, *Obj, Ctx, DisAsm.get(), SecondaryDisAsm.get(), - MIA.get(), IP.get(), STI.get(), SecondarySTI.get(), PIP, SP, - InlineRelocs); + disassembleObject(TheTarget, *Obj, *DbgObj, Ctx, DisAsm.get(), + SecondaryDisAsm.get(), MIA.get(), IP.get(), STI.get(), + SecondarySTI.get(), PIP, SP, InlineRelocs); } void objdump::printRelocations(const ObjectFile *Obj) { @@ -2026,6 +2260,9 @@ static size_t getMaxSectionNameWidth(const ObjectFile &Obj) { } void objdump::printSectionHeaders(ObjectFile &Obj) { + if (Obj.isELF() && Obj.sections().empty()) + createFakeELFSections(Obj); + size_t NameWidth = getMaxSectionNameWidth(Obj); size_t AddressWidth = 2 * Obj.getBytesInAddress(); bool HasLMAColumn = shouldDisplayLMA(Obj); @@ -2038,9 +2275,6 @@ void objdump::printSectionHeaders(ObjectFile &Obj) { outs() << "Idx " << left_justify("Name", NameWidth) << " Size " << left_justify("VMA", AddressWidth) << " Type\n"; - if (Obj.isELF() && Obj.sections().empty()) - createFakeELFSections(Obj); - uint64_t Idx; for (const SectionRef &Section : ToolSectionFilter(Obj, &Idx)) { StringRef Name = unwrapOrError(Section.getName(), Obj.getFileName()); @@ -2267,7 +2501,7 @@ void objdump::printSymbol(const ObjectFile &O, const SymbolRef &Symbol, StringRef SectionName = unwrapOrError(Section->getName(), FileName); outs() << SectionName; if (O.isXCOFF()) { - Optional<SymbolRef> SymRef = + std::optional<SymbolRef> SymRef = getXCOFFSymbolContainingSymbolRef(cast<XCOFFObjectFile>(O), Symbol); if (SymRef) { @@ -2281,8 +2515,8 @@ void objdump::printSymbol(const ObjectFile &O, const SymbolRef &Symbol, SymName = demangle(SymName); if (SymbolDescription) - SymName = getXCOFFSymbolDescription( - createSymbolInfo(O, SymRef.value()), SymName); + SymName = getXCOFFSymbolDescription(createSymbolInfo(O, *SymRef), + SymName); outs() << ' ' << SymName; outs() << ") "; @@ -2373,7 +2607,7 @@ static void printRawClangAST(const ObjectFile *Obj) { ClangASTSectionName = "clangast"; } - Optional<object::SectionRef> ClangASTSection; + std::optional<object::SectionRef> ClangASTSection; for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; if (Expected<StringRef> NameOrErr = Sec.getName()) @@ -2390,7 +2624,7 @@ static void printRawClangAST(const ObjectFile *Obj) { return; StringRef ClangASTContents = - unwrapOrError(ClangASTSection.value().getContents(), Obj->getFileName()); + unwrapOrError(ClangASTSection->getContents(), Obj->getFileName()); outs().write(ClangASTContents.data(), ClangASTContents.size()); } @@ -2408,7 +2642,7 @@ static void printFaultMaps(const ObjectFile *Obj) { return; } - Optional<object::SectionRef> FaultMapSection; + std::optional<object::SectionRef> FaultMapSection; for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; @@ -2705,7 +2939,18 @@ static void parseIntArg(const llvm::opt::InputArgList &InputArgs, int ID, } } -static void invalidArgValue(const opt::Arg *A) { +static object::BuildID parseBuildIDArg(const opt::Arg *A) { + StringRef V(A->getValue()); + std::string Bytes; + if (!tryGetFromHex(V, Bytes)) + reportCmdLineError(A->getSpelling() + ": expected a build ID, but got '" + + V + "'"); + ArrayRef<uint8_t> BuildID(reinterpret_cast<const uint8_t *>(Bytes.data()), + Bytes.size()); + return object::BuildID(BuildID.begin(), BuildID.end()); +} + +void objdump::invalidArgValue(const opt::Arg *A) { reportCmdLineError("'" + StringRef(A->getValue()) + "' is not a valid value for '" + A->getSpelling() + "'"); } @@ -2757,6 +3002,9 @@ static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) { FilterSections.push_back(",__text"); LeadingAddr = LeadingHeaders = !InputArgs.hasArg(OTOOL_X); + ChainedFixups = InputArgs.hasArg(OTOOL_chained_fixups); + DyldInfo = InputArgs.hasArg(OTOOL_dyld_info); + InputFilenames = InputArgs.getAllArgValues(OTOOL_INPUT); if (InputFilenames.empty()) reportCmdLineError("no input file"); @@ -2804,10 +3052,11 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { RawClangAST = InputArgs.hasArg(OBJDUMP_raw_clang_ast); Relocations = InputArgs.hasArg(OBJDUMP_reloc); PrintImmHex = - InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, false); + InputArgs.hasFlag(OBJDUMP_print_imm_hex, OBJDUMP_no_print_imm_hex, true); PrivateHeaders = InputArgs.hasArg(OBJDUMP_private_headers); FilterSections = InputArgs.getAllArgValues(OBJDUMP_section_EQ); SectionHeaders = InputArgs.hasArg(OBJDUMP_section_headers); + ShowAllSymbols = InputArgs.hasArg(OBJDUMP_show_all_symbols); ShowLMA = InputArgs.hasArg(OBJDUMP_show_lma); PrintSource = InputArgs.hasArg(OBJDUMP_source); parseIntArg(InputArgs, OBJDUMP_start_address_EQ, StartAddress); @@ -2869,6 +3118,17 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { llvm::cl::ParseCommandLineOptions(2, Argv); } + // Look up any provided build IDs, then append them to the input filenames. + for (const opt::Arg *A : InputArgs.filtered(OBJDUMP_build_id)) { + object::BuildID BuildID = parseBuildIDArg(A); + std::optional<std::string> Path = BIDFetcher->fetch(BuildID); + if (!Path) { + reportCmdLineError(A->getSpelling() + ": could not find build ID '" + + A->getValue() + "'"); + } + InputFilenames.push_back(std::move(*Path)); + } + // objdump defaults to a.out if no filenames specified. if (InputFilenames.empty()) InputFilenames.push_back("a.out"); @@ -2936,6 +3196,23 @@ int main(int argc, char **argv) { return 0; } + // Initialize debuginfod. + const bool ShouldUseDebuginfodByDefault = + InputArgs.hasArg(OBJDUMP_build_id) || + (HTTPClient::isAvailable() && + !ExitOnErr(getDefaultDebuginfodUrls()).empty()); + std::vector<std::string> DebugFileDirectories = + InputArgs.getAllArgValues(OBJDUMP_debug_file_directory); + if (InputArgs.hasFlag(OBJDUMP_debuginfod, OBJDUMP_no_debuginfod, + ShouldUseDebuginfodByDefault)) { + HTTPClient::initialize(); + BIDFetcher = + std::make_unique<DebuginfodFetcher>(std::move(DebugFileDirectories)); + } else { + BIDFetcher = + std::make_unique<BuildIDFetcher>(std::move(DebugFileDirectories)); + } + if (Is("otool")) parseOtoolOptions(InputArgs); else @@ -2960,11 +3237,12 @@ int main(int argc, char **argv) { !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading && - !(MachOOpt && (Bind || DataInCode || DyldInfo || DylibId || DylibsUsed || - ExportsTrie || FirstPrivateHeader || FunctionStarts || - IndirectSymbols || InfoPlist || LazyBind || LinkOptHints || - ObjcMetaData || Rebase || Rpaths || UniversalHeaders || - WeakBind || !FilterSections.empty()))) { + !(MachOOpt && + (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId || + DylibsUsed || ExportsTrie || FirstPrivateHeader || + FunctionStartsType != FunctionStartsMode::None || IndirectSymbols || + InfoPlist || LazyBind || LinkOptHints || ObjcMetaData || Rebase || + Rpaths || UniversalHeaders || WeakBind || !FilterSections.empty()))) { T->printHelp(ToolName); return 2; } diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h index c64c042d513e..efb445195259 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.h +++ b/llvm/tools/llvm-objdump/llvm-objdump.h @@ -20,6 +20,10 @@ namespace llvm { class StringRef; class Twine; +namespace opt { +class Arg; +} // namespace opt + namespace object { class RelocationRef; struct VersionEntry; @@ -146,6 +150,8 @@ T unwrapOrError(Expected<T> EO, Ts &&... Args) { reportError(EO.takeError(), std::forward<Ts>(Args)...); } +void invalidArgValue(const opt::Arg *A); + std::string getFileNameForError(const object::Archive::Child &C, unsigned Index); SymbolInfoTy createSymbolInfo(const object::ObjectFile &Obj, |