//===-- llvm-nm.cpp - Symbol table dumping utility for llvm ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This program is a utility that works like traditional Unix "nm", that is, it // prints out the names of symbols in a bitcode or object file, along with some // information about each symbol. // // This "nm" supports many of the features of GNU "nm", including its different // output formats. // //===----------------------------------------------------------------------===// #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Object/TapiFile.h" #include "llvm/Object/TapiUniversal.h" #include "llvm/Object/Wasm.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/LLVMDriver.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Triple.h" #include using namespace llvm; using namespace object; namespace { using namespace llvm::opt; // for HelpHidden in Opts.inc enum ID { OPT_INVALID = 0, // This is not an option ID. #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), #include "Opts.inc" #undef OPTION }; #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "Opts.inc" #undef PREFIX static constexpr opt::OptTable::Info InfoTable[] = { #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), #include "Opts.inc" #undef OPTION }; class NmOptTable : public opt::GenericOptTable { public: NmOptTable() : opt::GenericOptTable(InfoTable) { setGroupedShortOptions(true); } }; enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; enum class BitModeTy { Bit32, Bit64, Bit32_64, Any }; } // namespace static bool ArchiveMap; static BitModeTy BitMode; static bool DebugSyms; static bool DefinedOnly; static bool Demangle; static bool DynamicSyms; static bool ExportSymbols; static bool ExternalOnly; static bool LineNumbers; static OutputFormatTy OutputFormat; static bool NoLLVMBitcode; static bool NoSort; static bool NoWeakSymbols; static bool NumericSort; static bool PrintFileName; static bool PrintSize; static bool Quiet; static bool ReverseSort; static bool SpecialSyms; static bool SizeSort; static bool UndefinedOnly; static bool WithoutAliases; // XCOFF-specific options. static bool NoRsrc; namespace { enum Radix { d, o, x }; } // namespace static Radix AddressRadix; // Mach-O specific options. static bool ArchAll = false; static std::vector ArchFlags; static bool AddDyldInfo; static bool AddInlinedInfo; static bool DyldInfoOnly; static bool FormatMachOasHex; static bool NoDyldInfo; static std::vector SegSect; static bool MachOPrintSizeWarning = false; // Miscellaneous states. static bool PrintAddress = true; static bool MultipleFiles = false; static bool HadError = false; static StringRef ToolName; static void warn(Error Err, Twine FileName, Twine Context = Twine(), Twine Archive = Twine()) { assert(Err); // Flush the standard output so that the warning isn't interleaved with other // output if stdout and stderr are writing to the same place. outs().flush(); handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { WithColor::warning(errs(), ToolName) << (Archive.str().empty() ? FileName : Archive + "(" + FileName + ")") << ": " << (Context.str().empty() ? "" : Context + ": ") << EI.message() << "\n"; }); } static void error(Twine Message, Twine Path = Twine()) { HadError = true; WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n"; } static bool error(std::error_code EC, Twine Path = Twine()) { if (EC) { error(EC.message(), Path); return true; } return false; } // This version of error() prints the archive name and member name, for example: // "libx.a(foo.o)" after the ToolName before the error message. It sets // HadError but returns allowing the code to move on to other archive members. static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, StringRef ArchitectureName = StringRef()) { HadError = true; WithColor::error(errs(), ToolName) << FileName; Expected NameOrErr = C.getName(); // TODO: if we have a error getting the name then it would be nice to print // the index of which archive member this is and or its offset in the // archive instead of "???" as the name. if (!NameOrErr) { consumeError(NameOrErr.takeError()); errs() << "(" << "???" << ")"; } else errs() << "(" << NameOrErr.get() << ")"; if (!ArchitectureName.empty()) errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << ": " << Buf << "\n"; } // This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName // before the error message. It sets HadError but returns allowing the code to // move on to other architecture slices. static void error(llvm::Error E, StringRef FileName, StringRef ArchitectureName = StringRef()) { HadError = true; WithColor::error(errs(), ToolName) << FileName; if (!ArchitectureName.empty()) errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << ": " << Buf << "\n"; } namespace { struct NMSymbol { uint64_t Address; uint64_t Size; char TypeChar; std::string Name; StringRef SectionName; StringRef TypeName; BasicSymbolRef Sym; StringRef Visibility; // The Sym field above points to the native symbol in the object file, // for Mach-O when we are creating symbols from the dyld info the above // pointer is null as there is no native symbol. In these cases the fields // below are filled in to represent what would have been a Mach-O nlist // native symbol. uint32_t SymFlags; SectionRef Section; uint8_t NType; uint8_t NSect; uint16_t NDesc; std::string IndirectName; bool isDefined() const { if (Sym.getRawDataRefImpl().p) return !(SymFlags & SymbolRef::SF_Undefined); return TypeChar != 'U'; } bool initializeFlags(const SymbolicFile &Obj) { Expected SymFlagsOrErr = Sym.getFlags(); if (!SymFlagsOrErr) { // TODO: Test this error. error(SymFlagsOrErr.takeError(), Obj.getFileName()); return false; } SymFlags = *SymFlagsOrErr; return true; } bool shouldPrint() const { bool Undefined = SymFlags & SymbolRef::SF_Undefined; bool Global = SymFlags & SymbolRef::SF_Global; bool Weak = SymFlags & SymbolRef::SF_Weak; bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific; if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) || (!Global && ExternalOnly) || (Weak && NoWeakSymbols) || (FormatSpecific && !(SpecialSyms || DebugSyms))) return false; return true; } }; bool operator<(const NMSymbol &A, const NMSymbol &B) { if (NumericSort) return std::make_tuple(A.isDefined(), A.Address, A.Name, A.Size) < std::make_tuple(B.isDefined(), B.Address, B.Name, B.Size); if (SizeSort) return std::make_tuple(A.Size, A.Name, A.Address) < std::make_tuple(B.Size, B.Name, B.Address); if (ExportSymbols) return std::make_tuple(A.Name, A.Visibility) < std::make_tuple(B.Name, B.Visibility); return std::make_tuple(A.Name, A.Size, A.Address) < std::make_tuple(B.Name, B.Size, B.Address); } bool operator>(const NMSymbol &A, const NMSymbol &B) { return B < A; } bool operator==(const NMSymbol &A, const NMSymbol &B) { return !(A < B) && !(B < A); } } // anonymous namespace static StringRef CurrentFilename; static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I); // darwinPrintSymbol() is used to print a symbol from a Mach-O file when the // the OutputFormat is darwin or we are printing Mach-O symbols in hex. For // the darwin format it produces the same output as darwin's nm(1) -m output // and when printing Mach-O symbols in hex it produces the same output as // darwin's nm(1) -x format. static void darwinPrintSymbol(SymbolicFile &Obj, const NMSymbol &S, char *SymbolAddrStr, const char *printBlanks, const char *printDashes, const char *printFormat) { MachO::mach_header H; MachO::mach_header_64 H_64; uint32_t Filetype = MachO::MH_OBJECT; uint32_t Flags = 0; uint8_t NType = 0; uint8_t NSect = 0; uint16_t NDesc = 0; uint32_t NStrx = 0; uint64_t NValue = 0; MachOObjectFile *MachO = dyn_cast(&Obj); if (Obj.isIR()) { uint32_t SymFlags = cantFail(S.Sym.getFlags()); if (SymFlags & SymbolRef::SF_Global) NType |= MachO::N_EXT; if (SymFlags & SymbolRef::SF_Hidden) NType |= MachO::N_PEXT; if (SymFlags & SymbolRef::SF_Undefined) NType |= MachO::N_EXT | MachO::N_UNDF; else { // Here we have a symbol definition. So to fake out a section name we // use 1, 2 and 3 for section numbers. See below where they are used to // print out fake section names. NType |= MachO::N_SECT; if (SymFlags & SymbolRef::SF_Const) NSect = 3; else if (SymFlags & SymbolRef::SF_Executable) NSect = 1; else NSect = 2; } if (SymFlags & SymbolRef::SF_Weak) NDesc |= MachO::N_WEAK_DEF; } else { DataRefImpl SymDRI = S.Sym.getRawDataRefImpl(); if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); Filetype = H_64.filetype; Flags = H_64.flags; if (SymDRI.p){ MachO::nlist_64 STE_64 = MachO->getSymbol64TableEntry(SymDRI); NType = STE_64.n_type; NSect = STE_64.n_sect; NDesc = STE_64.n_desc; NStrx = STE_64.n_strx; NValue = STE_64.n_value; } else { NType = S.NType; NSect = S.NSect; NDesc = S.NDesc; NStrx = 0; NValue = S.Address; } } else { H = MachO->MachOObjectFile::getHeader(); Filetype = H.filetype; Flags = H.flags; if (SymDRI.p){ MachO::nlist STE = MachO->getSymbolTableEntry(SymDRI); NType = STE.n_type; NSect = STE.n_sect; NDesc = STE.n_desc; NStrx = STE.n_strx; NValue = STE.n_value; } else { NType = S.NType; NSect = S.NSect; NDesc = S.NDesc; NStrx = 0; NValue = S.Address; } } } // If we are printing Mach-O symbols in hex do that and return. if (FormatMachOasHex) { outs() << format(printFormat, NValue) << ' ' << format("%02x %02x %04x %08x", NType, NSect, NDesc, NStrx) << ' ' << S.Name; if ((NType & MachO::N_TYPE) == MachO::N_INDR) { outs() << " (indirect for "; outs() << format(printFormat, NValue) << ' '; StringRef IndirectName; if (S.Sym.getRawDataRefImpl().p) { if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; } else outs() << S.IndirectName << ")"; } outs() << "\n"; return; } if (PrintAddress) { if ((NType & MachO::N_TYPE) == MachO::N_INDR) strcpy(SymbolAddrStr, printBlanks); if (Obj.isIR() && (NType & MachO::N_TYPE) == MachO::N_TYPE) strcpy(SymbolAddrStr, printDashes); outs() << SymbolAddrStr << ' '; } switch (NType & MachO::N_TYPE) { case MachO::N_UNDF: if (NValue != 0) { outs() << "(common) "; if (MachO::GET_COMM_ALIGN(NDesc) != 0) outs() << "(alignment 2^" << (int)MachO::GET_COMM_ALIGN(NDesc) << ") "; } else { if ((NType & MachO::N_TYPE) == MachO::N_PBUD) outs() << "(prebound "; else outs() << "("; if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY) outs() << "undefined [lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY) outs() << "undefined [private lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY) outs() << "undefined [private]) "; else outs() << "undefined) "; } break; case MachO::N_ABS: outs() << "(absolute) "; break; case MachO::N_INDR: outs() << "(indirect) "; break; case MachO::N_SECT: { if (Obj.isIR()) { // For llvm bitcode files print out a fake section name using the values // use 1, 2 and 3 for section numbers as set above. if (NSect == 1) outs() << "(LTO,CODE) "; else if (NSect == 2) outs() << "(LTO,DATA) "; else if (NSect == 3) outs() << "(LTO,RODATA) "; else outs() << "(?,?) "; break; } section_iterator Sec = SectionRef(); if (S.Sym.getRawDataRefImpl().p) { Expected SecOrErr = MachO->getSymbolSection(S.Sym.getRawDataRefImpl()); if (!SecOrErr) { consumeError(SecOrErr.takeError()); outs() << "(?,?) "; break; } Sec = *SecOrErr; if (Sec == MachO->section_end()) { outs() << "(?,?) "; break; } } else { Sec = S.Section; } DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; if (Expected NameOrErr = MachO->getSectionName(Ref)) SectionName = *NameOrErr; StringRef SegmentName = MachO->getSectionFinalSegmentName(Ref); outs() << "(" << SegmentName << "," << SectionName << ") "; break; } default: outs() << "(?) "; break; } if (NType & MachO::N_EXT) { if (NDesc & MachO::REFERENCED_DYNAMICALLY) outs() << "[referenced dynamically] "; if (NType & MachO::N_PEXT) { if ((NDesc & MachO::N_WEAK_DEF) == MachO::N_WEAK_DEF) outs() << "weak private external "; else outs() << "private external "; } else { if ((NDesc & MachO::N_WEAK_REF) == MachO::N_WEAK_REF || (NDesc & MachO::N_WEAK_DEF) == MachO::N_WEAK_DEF) { if ((NDesc & (MachO::N_WEAK_REF | MachO::N_WEAK_DEF)) == (MachO::N_WEAK_REF | MachO::N_WEAK_DEF)) outs() << "weak external automatically hidden "; else outs() << "weak external "; } else outs() << "external "; } } else { if (NType & MachO::N_PEXT) outs() << "non-external (was a private external) "; else outs() << "non-external "; } if (Filetype == MachO::MH_OBJECT) { if (NDesc & MachO::N_NO_DEAD_STRIP) outs() << "[no dead strip] "; if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_SYMBOL_RESOLVER) outs() << "[symbol resolver] "; if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_ALT_ENTRY) outs() << "[alt entry] "; if ((NType & MachO::N_TYPE) != MachO::N_UNDF && NDesc & MachO::N_COLD_FUNC) outs() << "[cold func] "; } if ((NDesc & MachO::N_ARM_THUMB_DEF) == MachO::N_ARM_THUMB_DEF) outs() << "[Thumb] "; if ((NType & MachO::N_TYPE) == MachO::N_INDR) { outs() << S.Name << " (for "; StringRef IndirectName; if (MachO) { if (S.Sym.getRawDataRefImpl().p) { if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; } else outs() << S.IndirectName << ")"; } else outs() << "?)"; } else outs() << S.Name; if ((Flags & MachO::MH_TWOLEVEL) == MachO::MH_TWOLEVEL && (((NType & MachO::N_TYPE) == MachO::N_UNDF && NValue == 0) || (NType & MachO::N_TYPE) == MachO::N_PBUD)) { uint32_t LibraryOrdinal = MachO::GET_LIBRARY_ORDINAL(NDesc); if (LibraryOrdinal != 0) { if (LibraryOrdinal == MachO::EXECUTABLE_ORDINAL) outs() << " (from executable)"; else if (LibraryOrdinal == MachO::DYNAMIC_LOOKUP_ORDINAL) outs() << " (dynamically looked up)"; else { StringRef LibraryName; if (!MachO || MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName)) outs() << " (from bad library ordinal " << LibraryOrdinal << ")"; else outs() << " (from " << LibraryName << ")"; } } } } // Table that maps Darwin's Mach-O stab constants to strings to allow printing. struct DarwinStabName { uint8_t NType; const char *Name; }; const struct DarwinStabName DarwinStabNames[] = { {MachO::N_GSYM, "GSYM"}, {MachO::N_FNAME, "FNAME"}, {MachO::N_FUN, "FUN"}, {MachO::N_STSYM, "STSYM"}, {MachO::N_LCSYM, "LCSYM"}, {MachO::N_BNSYM, "BNSYM"}, {MachO::N_PC, "PC"}, {MachO::N_AST, "AST"}, {MachO::N_OPT, "OPT"}, {MachO::N_RSYM, "RSYM"}, {MachO::N_SLINE, "SLINE"}, {MachO::N_ENSYM, "ENSYM"}, {MachO::N_SSYM, "SSYM"}, {MachO::N_SO, "SO"}, {MachO::N_OSO, "OSO"}, {MachO::N_LIB, "LIB"}, {MachO::N_LSYM, "LSYM"}, {MachO::N_BINCL, "BINCL"}, {MachO::N_SOL, "SOL"}, {MachO::N_PARAMS, "PARAM"}, {MachO::N_VERSION, "VERS"}, {MachO::N_OLEVEL, "OLEV"}, {MachO::N_PSYM, "PSYM"}, {MachO::N_EINCL, "EINCL"}, {MachO::N_ENTRY, "ENTRY"}, {MachO::N_LBRAC, "LBRAC"}, {MachO::N_EXCL, "EXCL"}, {MachO::N_RBRAC, "RBRAC"}, {MachO::N_BCOMM, "BCOMM"}, {MachO::N_ECOMM, "ECOMM"}, {MachO::N_ECOML, "ECOML"}, {MachO::N_LENG, "LENG"}, }; static const char *getDarwinStabString(uint8_t NType) { for (auto I : ArrayRef(DarwinStabNames)) if (I.NType == NType) return I.Name; return nullptr; } // darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of // a stab n_type value in a Mach-O file. static void darwinPrintStab(MachOObjectFile *MachO, const NMSymbol &S) { MachO::nlist_64 STE_64; MachO::nlist STE; uint8_t NType; uint8_t NSect; uint16_t NDesc; DataRefImpl SymDRI = S.Sym.getRawDataRefImpl(); if (MachO->is64Bit()) { STE_64 = MachO->getSymbol64TableEntry(SymDRI); NType = STE_64.n_type; NSect = STE_64.n_sect; NDesc = STE_64.n_desc; } else { STE = MachO->getSymbolTableEntry(SymDRI); NType = STE.n_type; NSect = STE.n_sect; NDesc = STE.n_desc; } outs() << format(" %02x %04x ", NSect, NDesc); if (const char *stabString = getDarwinStabString(NType)) outs() << format("%5.5s", stabString); else outs() << format(" %02x", NType); } static bool symbolIsDefined(const NMSymbol &Sym) { return Sym.TypeChar != 'U' && Sym.TypeChar != 'w' && Sym.TypeChar != 'v'; } static void writeFileName(raw_ostream &S, StringRef ArchiveName, StringRef ArchitectureName) { if (!ArchitectureName.empty()) S << "(for architecture " << ArchitectureName << "):"; if (OutputFormat == posix && !ArchiveName.empty()) S << ArchiveName << "[" << CurrentFilename << "]: "; else { if (!ArchiveName.empty()) S << ArchiveName << ":"; S << CurrentFilename << ": "; } } static void sortSymbolList(std::vector &SymbolList) { if (NoSort) return; if (ReverseSort) llvm::sort(SymbolList, std::greater<>()); else llvm::sort(SymbolList); } static void printExportSymbolList(const std::vector &SymbolList) { for (const NMSymbol &Sym : SymbolList) { outs() << Sym.Name; if (!Sym.Visibility.empty()) outs() << ' ' << Sym.Visibility; outs() << '\n'; } } static void printLineNumbers(symbolize::LLVMSymbolizer &Symbolizer, const NMSymbol &S) { const auto *Obj = dyn_cast(S.Sym.getObject()); if (!Obj) return; const SymbolRef Sym(S.Sym); uint64_t SectionIndex = object::SectionedAddress::UndefSection; section_iterator Sec = cantFail(Sym.getSection()); if (Sec != Obj->section_end()) SectionIndex = Sec->getIndex(); object::SectionedAddress Address = {cantFail(Sym.getAddress()), SectionIndex}; std::string FileName; uint32_t Line; switch (S.TypeChar) { // For undefined symbols, find the first relocation for that symbol with a // line number. case 'U': { for (const SectionRef RelocsSec : Obj->sections()) { if (RelocsSec.relocations().empty()) continue; SectionRef TextSec = *cantFail(RelocsSec.getRelocatedSection()); if (!TextSec.isText()) continue; for (const RelocationRef R : RelocsSec.relocations()) { if (R.getSymbol() != Sym) continue; Expected ResOrErr = Symbolizer.symbolizeCode( *Obj, {TextSec.getAddress() + R.getOffset(), SectionIndex}); if (!ResOrErr) { error(ResOrErr.takeError(), Obj->getFileName()); return; } if (ResOrErr->FileName == DILineInfo::BadString) return; FileName = std::move(ResOrErr->FileName); Line = ResOrErr->Line; break; } if (!FileName.empty()) break; } if (FileName.empty()) return; break; } case 't': case 'T': { Expected ResOrErr = Symbolizer.symbolizeCode(*Obj, Address); if (!ResOrErr) { error(ResOrErr.takeError(), Obj->getFileName()); return; } if (ResOrErr->FileName == DILineInfo::BadString) return; FileName = std::move(ResOrErr->FileName); Line = ResOrErr->Line; break; } default: { Expected ResOrErr = Symbolizer.symbolizeData(*Obj, Address); if (!ResOrErr) { error(ResOrErr.takeError(), Obj->getFileName()); return; } if (ResOrErr->DeclFile.empty()) return; FileName = std::move(ResOrErr->DeclFile); Line = ResOrErr->DeclLine; break; } } outs() << '\t' << FileName << ':' << Line; } static void printSymbolList(SymbolicFile &Obj, std::vector &SymbolList, bool printName, StringRef ArchiveName, StringRef ArchitectureName) { std::optional Symbolizer; if (LineNumbers) Symbolizer.emplace(); if (!PrintFileName) { if ((OutputFormat == bsd || OutputFormat == posix || OutputFormat == just_symbols) && MultipleFiles && printName) { outs() << '\n' << CurrentFilename << ":\n"; } else if (OutputFormat == sysv) { outs() << "\n\nSymbols from " << CurrentFilename << ":\n\n"; if (Obj.is64Bit()) outs() << "Name Value Class Type" << " Size Line Section\n"; else outs() << "Name Value Class Type" << " Size Line Section\n"; } } const char *printBlanks, *printDashes, *printFormat; if (Obj.is64Bit()) { printBlanks = " "; printDashes = "----------------"; switch (AddressRadix) { case Radix::o: printFormat = OutputFormat == posix ? "%" PRIo64 : "%016" PRIo64; break; case Radix::x: printFormat = OutputFormat == posix ? "%" PRIx64 : "%016" PRIx64; break; default: printFormat = OutputFormat == posix ? "%" PRId64 : "%016" PRId64; } } else { printBlanks = " "; printDashes = "--------"; switch (AddressRadix) { case Radix::o: printFormat = OutputFormat == posix ? "%" PRIo64 : "%08" PRIo64; break; case Radix::x: printFormat = OutputFormat == posix ? "%" PRIx64 : "%08" PRIx64; break; default: printFormat = OutputFormat == posix ? "%" PRId64 : "%08" PRId64; } } for (const NMSymbol &S : SymbolList) { if (!S.shouldPrint()) continue; std::string Name = S.Name; MachOObjectFile *MachO = dyn_cast(&Obj); if (Demangle) Name = demangle(Name); if (PrintFileName) writeFileName(outs(), ArchiveName, ArchitectureName); if ((OutputFormat == just_symbols || (UndefinedOnly && MachO && OutputFormat != darwin)) && OutputFormat != posix) { outs() << Name << "\n"; continue; } char SymbolAddrStr[23], SymbolSizeStr[23]; // If the format is SysV or the symbol isn't defined, then print spaces. if (OutputFormat == sysv || !symbolIsDefined(S)) { if (OutputFormat == posix) { format(printFormat, S.Address) .print(SymbolAddrStr, sizeof(SymbolAddrStr)); format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); } else { strcpy(SymbolAddrStr, printBlanks); strcpy(SymbolSizeStr, printBlanks); } } if (symbolIsDefined(S)) { // Otherwise, print the symbol address and size. if (Obj.isIR()) strcpy(SymbolAddrStr, printDashes); else if (MachO && S.TypeChar == 'I') strcpy(SymbolAddrStr, printBlanks); else format(printFormat, S.Address) .print(SymbolAddrStr, sizeof(SymbolAddrStr)); format(printFormat, S.Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); } // If OutputFormat is darwin or we are printing Mach-O symbols in hex and // we have a MachOObjectFile, call darwinPrintSymbol to print as darwin's // nm(1) -m output or hex, else if OutputFormat is darwin or we are // printing Mach-O symbols in hex and not a Mach-O object fall back to // OutputFormat bsd (see below). if ((OutputFormat == darwin || FormatMachOasHex) && (MachO || Obj.isIR())) { darwinPrintSymbol(Obj, S, SymbolAddrStr, printBlanks, printDashes, printFormat); } else if (OutputFormat == posix) { outs() << Name << " " << S.TypeChar << " " << SymbolAddrStr << " " << (MachO ? "0" : SymbolSizeStr); } else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) { if (PrintAddress) outs() << SymbolAddrStr << ' '; if (PrintSize) outs() << SymbolSizeStr << ' '; outs() << S.TypeChar; if (S.TypeChar == '-' && MachO) darwinPrintStab(MachO, S); outs() << " " << Name; if (S.TypeChar == 'I' && MachO) { outs() << " (indirect for "; if (S.Sym.getRawDataRefImpl().p) { StringRef IndirectName; if (MachO->getIndirectName(S.Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; } else outs() << S.IndirectName << ")"; } } else if (OutputFormat == sysv) { outs() << left_justify(Name, 20) << "|" << SymbolAddrStr << "| " << S.TypeChar << " |" << right_justify(S.TypeName, 18) << "|" << SymbolSizeStr << "| |" << S.SectionName; } if (LineNumbers) printLineNumbers(*Symbolizer, S); outs() << '\n'; } SymbolList.clear(); } static char getSymbolNMTypeChar(ELFObjectFileBase &Obj, basic_symbol_iterator I) { // OK, this is ELF elf_symbol_iterator SymI(I); Expected SecIOrErr = SymI->getSection(); if (!SecIOrErr) { consumeError(SecIOrErr.takeError()); return '?'; } uint8_t Binding = SymI->getBinding(); if (Binding == ELF::STB_GNU_UNIQUE) return 'u'; assert(Binding != ELF::STB_WEAK && "STB_WEAK not tested in calling function"); if (Binding != ELF::STB_GLOBAL && Binding != ELF::STB_LOCAL) return '?'; elf_section_iterator SecI = *SecIOrErr; if (SecI != Obj.section_end()) { uint32_t Type = SecI->getType(); uint64_t Flags = SecI->getFlags(); if (Flags & ELF::SHF_EXECINSTR) return 't'; if (Type == ELF::SHT_NOBITS) return 'b'; if (Flags & ELF::SHF_ALLOC) return Flags & ELF::SHF_WRITE ? 'd' : 'r'; auto NameOrErr = SecI->getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); return '?'; } if ((*NameOrErr).starts_with(".debug")) return 'N'; if (!(Flags & ELF::SHF_WRITE)) return 'n'; } return '?'; } static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { COFFSymbolRef Symb = Obj.getCOFFSymbol(*I); // OK, this is COFF. symbol_iterator SymI(I); Expected Name = SymI->getName(); if (!Name) { consumeError(Name.takeError()); return '?'; } char Ret = StringSwitch(*Name) .StartsWith(".debug", 'N') .StartsWith(".sxdata", 'N') .Default('?'); if (Ret != '?') return Ret; uint32_t Characteristics = 0; if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) { Expected SecIOrErr = SymI->getSection(); if (!SecIOrErr) { consumeError(SecIOrErr.takeError()); return '?'; } section_iterator SecI = *SecIOrErr; const coff_section *Section = Obj.getCOFFSection(*SecI); Characteristics = Section->Characteristics; if (Expected NameOrErr = Obj.getSectionName(Section)) if (NameOrErr->starts_with(".idata")) return 'i'; } switch (Symb.getSectionNumber()) { case COFF::IMAGE_SYM_DEBUG: return 'n'; default: // Check section type. if (Characteristics & COFF::IMAGE_SCN_CNT_CODE) return 't'; if (Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) return Characteristics & COFF::IMAGE_SCN_MEM_WRITE ? 'd' : 'r'; if (Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) return 'b'; if (Characteristics & COFF::IMAGE_SCN_LNK_INFO) return 'i'; // Check for section symbol. if (Symb.isSectionDefinition()) return 's'; } return '?'; } static char getSymbolNMTypeChar(XCOFFObjectFile &Obj, symbol_iterator I) { Expected TypeOrErr = I->getType(); if (!TypeOrErr) { warn(TypeOrErr.takeError(), Obj.getFileName(), "for symbol with index " + Twine(Obj.getSymbolIndex(I->getRawDataRefImpl().p))); return '?'; } uint32_t SymType = *TypeOrErr; if (SymType == SymbolRef::ST_File) return 'f'; // If the I->getSection() call would return an error, the earlier I->getType() // call will already have returned the same error first. section_iterator SecIter = cantFail(I->getSection()); if (SecIter == Obj.section_end()) return '?'; if (Obj.isDebugSection(SecIter->getRawDataRefImpl())) return 'N'; if (SecIter->isText()) return 't'; if (SecIter->isData()) return 'd'; if (SecIter->isBSS()) return 'b'; return '?'; } static char getSymbolNMTypeChar(COFFImportFile &Obj) { switch (Obj.getCOFFImportHeader()->getType()) { case COFF::IMPORT_CODE: return 't'; case COFF::IMPORT_DATA: return 'd'; case COFF::IMPORT_CONST: return 'r'; } return '?'; } static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { DataRefImpl Symb = I->getRawDataRefImpl(); uint8_t NType = Obj.is64Bit() ? Obj.getSymbol64TableEntry(Symb).n_type : Obj.getSymbolTableEntry(Symb).n_type; if (NType & MachO::N_STAB) return '-'; switch (NType & MachO::N_TYPE) { case MachO::N_ABS: return 's'; case MachO::N_INDR: return 'i'; case MachO::N_SECT: { Expected SecOrErr = Obj.getSymbolSection(Symb); if (!SecOrErr) { consumeError(SecOrErr.takeError()); return 's'; } section_iterator Sec = *SecOrErr; if (Sec == Obj.section_end()) return 's'; DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; if (Expected NameOrErr = Obj.getSectionName(Ref)) SectionName = *NameOrErr; StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref); if (Obj.is64Bit() && Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE && SegmentName == "__TEXT_EXEC" && SectionName == "__text") return 't'; if (SegmentName == "__TEXT" && SectionName == "__text") return 't'; if (SegmentName == "__DATA" && SectionName == "__data") return 'd'; if (SegmentName == "__DATA" && SectionName == "__bss") return 'b'; return 's'; } } return '?'; } static char getSymbolNMTypeChar(TapiFile &Obj, basic_symbol_iterator I) { auto Type = cantFail(Obj.getSymbolType(I->getRawDataRefImpl())); switch (Type) { case SymbolRef::ST_Function: return 't'; case SymbolRef::ST_Data: if (Obj.hasSegmentInfo()) return 'd'; [[fallthrough]]; default: return 's'; } } static char getSymbolNMTypeChar(WasmObjectFile &Obj, basic_symbol_iterator I) { uint32_t Flags = cantFail(I->getFlags()); if (Flags & SymbolRef::SF_Executable) return 't'; return 'd'; } static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I) { uint32_t Flags = cantFail(I->getFlags()); // FIXME: should we print 'b'? At the IR level we cannot be sure if this // will be in bss or not, but we could approximate. if (Flags & SymbolRef::SF_Executable) return 't'; else if (Triple(Obj.getTargetTriple()).isOSDarwin() && (Flags & SymbolRef::SF_Const)) return 's'; else return 'd'; } static bool isObject(SymbolicFile &Obj, basic_symbol_iterator I) { return isa(&Obj) && elf_symbol_iterator(I)->getELFType() == ELF::STT_OBJECT; } // For ELF object files, Set TypeName to the symbol typename, to be printed // in the 'Type' column of the SYSV format output. static StringRef getNMTypeName(SymbolicFile &Obj, basic_symbol_iterator I) { if (isa(&Obj)) { elf_symbol_iterator SymI(I); return SymI->getELFTypeName(); } return ""; } // Return Posix nm class type tag (single letter), but also set SecName and // section and name, to be used in format=sysv output. static char getNMSectionTagAndName(SymbolicFile &Obj, basic_symbol_iterator I, StringRef &SecName) { // Symbol Flags have been checked in the caller. uint32_t Symflags = cantFail(I->getFlags()); if (ELFObjectFileBase *ELFObj = dyn_cast(&Obj)) { if (Symflags & object::SymbolRef::SF_Absolute) SecName = "*ABS*"; else if (Symflags & object::SymbolRef::SF_Common) SecName = "*COM*"; else if (Symflags & object::SymbolRef::SF_Undefined) SecName = "*UND*"; else { elf_symbol_iterator SymI(I); Expected SecIOrErr = SymI->getSection(); if (!SecIOrErr) { consumeError(SecIOrErr.takeError()); return '?'; } if (*SecIOrErr == ELFObj->section_end()) return '?'; Expected NameOrErr = (*SecIOrErr)->getName(); if (!NameOrErr) { consumeError(NameOrErr.takeError()); return '?'; } SecName = *NameOrErr; } } if (Symflags & object::SymbolRef::SF_Undefined) { if (isa(Obj) || !(Symflags & object::SymbolRef::SF_Weak)) return 'U'; return isObject(Obj, I) ? 'v' : 'w'; } if (isa(&Obj)) if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC) return 'i'; if (!isa(Obj) && (Symflags & object::SymbolRef::SF_Weak)) return isObject(Obj, I) ? 'V' : 'W'; if (Symflags & object::SymbolRef::SF_Common) return 'C'; char Ret = '?'; if (Symflags & object::SymbolRef::SF_Absolute) Ret = 'a'; else if (IRObjectFile *IR = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*IR, I); else if (COFFObjectFile *COFF = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*COFF, I); else if (XCOFFObjectFile *XCOFF = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*XCOFF, I); else if (COFFImportFile *COFFImport = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*COFFImport); else if (MachOObjectFile *MachO = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*MachO, I); else if (WasmObjectFile *Wasm = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*Wasm, I); else if (TapiFile *Tapi = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*Tapi, I); else if (ELFObjectFileBase *ELF = dyn_cast(&Obj)) { Ret = getSymbolNMTypeChar(*ELF, I); if (ELFSymbolRef(*I).getBinding() == ELF::STB_GNU_UNIQUE) return Ret; } else llvm_unreachable("unknown binary format"); if (!(Symflags & object::SymbolRef::SF_Global)) return Ret; return toupper(Ret); } // getNsectForSegSect() is used to implement the Mach-O "-s segname sectname" // option to dump only those symbols from that section in a Mach-O file. // It is called once for each Mach-O file from getSymbolNamesFromObject() // to get the section number for that named section from the command line // arguments. It returns the section number for that section in the Mach-O // file or zero it is not present. static unsigned getNsectForSegSect(MachOObjectFile *Obj) { unsigned Nsect = 1; for (auto &S : Obj->sections()) { DataRefImpl Ref = S.getRawDataRefImpl(); StringRef SectionName; if (Expected NameOrErr = Obj->getSectionName(Ref)) SectionName = *NameOrErr; StringRef SegmentName = Obj->getSectionFinalSegmentName(Ref); if (SegmentName == SegSect[0] && SectionName == SegSect[1]) return Nsect; Nsect++; } return 0; } // getNsectInMachO() is used to implement the Mach-O "-s segname sectname" // option to dump only those symbols from that section in a Mach-O file. // It is called once for each symbol in a Mach-O file from // getSymbolNamesFromObject() and returns the section number for that symbol // if it is in a section, else it returns 0. static unsigned getNsectInMachO(MachOObjectFile &Obj, BasicSymbolRef Sym) { DataRefImpl Symb = Sym.getRawDataRefImpl(); if (Obj.is64Bit()) { MachO::nlist_64 STE = Obj.getSymbol64TableEntry(Symb); return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0; } MachO::nlist STE = Obj.getSymbolTableEntry(Symb); return (STE.n_type & MachO::N_TYPE) == MachO::N_SECT ? STE.n_sect : 0; } static void dumpSymbolsFromDLInfoMachO(MachOObjectFile &MachO, std::vector &SymbolList) { size_t I = SymbolList.size(); std::string ExportsNameBuffer; raw_string_ostream EOS(ExportsNameBuffer); std::string BindsNameBuffer; raw_string_ostream BOS(BindsNameBuffer); std::string LazysNameBuffer; raw_string_ostream LOS(LazysNameBuffer); std::string WeaksNameBuffer; raw_string_ostream WOS(WeaksNameBuffer); std::string FunctionStartsNameBuffer; raw_string_ostream FOS(FunctionStartsNameBuffer); MachO::mach_header H; MachO::mach_header_64 H_64; uint32_t HFlags = 0; if (MachO.is64Bit()) { H_64 = MachO.MachOObjectFile::getHeader64(); HFlags = H_64.flags; } else { H = MachO.MachOObjectFile::getHeader(); HFlags = H.flags; } uint64_t BaseSegmentAddress = 0; for (const auto &Command : MachO.load_commands()) { if (Command.C.cmd == MachO::LC_SEGMENT) { MachO::segment_command Seg = MachO.getSegmentLoadCommand(Command); if (Seg.fileoff == 0 && Seg.filesize != 0) { BaseSegmentAddress = Seg.vmaddr; break; } } else if (Command.C.cmd == MachO::LC_SEGMENT_64) { MachO::segment_command_64 Seg = MachO.getSegment64LoadCommand(Command); if (Seg.fileoff == 0 && Seg.filesize != 0) { BaseSegmentAddress = Seg.vmaddr; break; } } } if (DyldInfoOnly || AddDyldInfo || HFlags & MachO::MH_NLIST_OUTOFSYNC_WITH_DYLDINFO) { unsigned ExportsAdded = 0; Error Err = Error::success(); for (const llvm::object::ExportEntry &Entry : MachO.exports(Err)) { bool found = false; bool ReExport = false; if (!DyldInfoOnly) { for (const NMSymbol &S : SymbolList) if (S.Address == Entry.address() + BaseSegmentAddress && S.Name == Entry.name()) { found = true; break; } } if (!found) { NMSymbol S = {}; S.Address = Entry.address() + BaseSegmentAddress; S.Size = 0; S.TypeChar = '\0'; S.Name = Entry.name().str(); // There is no symbol in the nlist symbol table for this so we set // Sym effectivly to null and the rest of code in here must test for // it and not do things like Sym.getFlags() for it. S.Sym = BasicSymbolRef(); S.SymFlags = SymbolRef::SF_Global; S.Section = SectionRef(); S.NType = 0; S.NSect = 0; S.NDesc = 0; uint64_t EFlags = Entry.flags(); bool Abs = ((EFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) == MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); bool Resolver = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); ReExport = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); bool WeakDef = (EFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); if (WeakDef) S.NDesc |= MachO::N_WEAK_DEF; if (Abs) { S.NType = MachO::N_EXT | MachO::N_ABS; S.TypeChar = 'A'; } else if (ReExport) { S.NType = MachO::N_EXT | MachO::N_INDR; S.TypeChar = 'I'; } else { S.NType = MachO::N_EXT | MachO::N_SECT; if (Resolver) { S.Address = Entry.other() + BaseSegmentAddress; if ((S.Address & 1) != 0 && !MachO.is64Bit() && H.cputype == MachO::CPU_TYPE_ARM) { S.Address &= ~1LL; S.NDesc |= MachO::N_ARM_THUMB_DEF; } } else { S.Address = Entry.address() + BaseSegmentAddress; } StringRef SegmentName = StringRef(); StringRef SectionName = StringRef(); for (const SectionRef &Section : MachO.sections()) { S.NSect++; if (Expected NameOrErr = Section.getName()) SectionName = *NameOrErr; else consumeError(NameOrErr.takeError()); SegmentName = MachO.getSectionFinalSegmentName(Section.getRawDataRefImpl()); if (S.Address >= Section.getAddress() && S.Address < Section.getAddress() + Section.getSize()) { S.Section = Section; break; } else if (Entry.name() == "__mh_execute_header" && SegmentName == "__TEXT" && SectionName == "__text") { S.Section = Section; S.NDesc |= MachO::REFERENCED_DYNAMICALLY; break; } } if (SegmentName == "__TEXT" && SectionName == "__text") S.TypeChar = 'T'; else if (SegmentName == "__DATA" && SectionName == "__data") S.TypeChar = 'D'; else if (SegmentName == "__DATA" && SectionName == "__bss") S.TypeChar = 'B'; else S.TypeChar = 'S'; } SymbolList.push_back(S); EOS << Entry.name(); EOS << '\0'; ExportsAdded++; // For ReExports there are a two more things to do, first add the // indirect name and second create the undefined symbol using the // referened dynamic library. if (ReExport) { // Add the indirect name. if (Entry.otherName().empty()) EOS << Entry.name(); else EOS << Entry.otherName(); EOS << '\0'; // Now create the undefined symbol using the referened dynamic // library. NMSymbol U = {}; U.Address = 0; U.Size = 0; U.TypeChar = 'U'; if (Entry.otherName().empty()) U.Name = Entry.name().str(); else U.Name = Entry.otherName().str(); // Again there is no symbol in the nlist symbol table for this so // we set Sym effectivly to null and the rest of code in here must // test for it and not do things like Sym.getFlags() for it. U.Sym = BasicSymbolRef(); U.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined; U.Section = SectionRef(); U.NType = MachO::N_EXT | MachO::N_UNDF; U.NSect = 0; U.NDesc = 0; // The library ordinal for this undefined symbol is in the export // trie Entry.other(). MachO::SET_LIBRARY_ORDINAL(U.NDesc, Entry.other()); SymbolList.push_back(U); // Finally add the undefined symbol's name. if (Entry.otherName().empty()) EOS << Entry.name(); else EOS << Entry.otherName(); EOS << '\0'; ExportsAdded++; } } } if (Err) error(std::move(Err), MachO.getFileName()); // Set the symbol names and indirect names for the added symbols. if (ExportsAdded) { EOS.flush(); const char *Q = ExportsNameBuffer.c_str(); for (unsigned K = 0; K < ExportsAdded; K++) { SymbolList[I].Name = Q; Q += strlen(Q) + 1; if (SymbolList[I].TypeChar == 'I') { SymbolList[I].IndirectName = Q; Q += strlen(Q) + 1; } I++; } } // Add the undefined symbols from the bind entries. unsigned BindsAdded = 0; Error BErr = Error::success(); StringRef LastSymbolName = StringRef(); for (const llvm::object::MachOBindEntry &Entry : MachO.bindTable(BErr)) { bool found = false; if (LastSymbolName == Entry.symbolName()) found = true; else if (!DyldInfoOnly) { for (unsigned J = 0; J < SymbolList.size() && !found; ++J) { if (SymbolList[J].Name == Entry.symbolName()) found = true; } } if (!found) { LastSymbolName = Entry.symbolName(); NMSymbol B = {}; B.Address = 0; B.Size = 0; B.TypeChar = 'U'; // There is no symbol in the nlist symbol table for this so we set // Sym effectivly to null and the rest of code in here must test for // it and not do things like Sym.getFlags() for it. B.Sym = BasicSymbolRef(); B.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined; B.NType = MachO::N_EXT | MachO::N_UNDF; B.NSect = 0; B.NDesc = 0; MachO::SET_LIBRARY_ORDINAL(B.NDesc, Entry.ordinal()); B.Name = Entry.symbolName().str(); SymbolList.push_back(B); BOS << Entry.symbolName(); BOS << '\0'; BindsAdded++; } } if (BErr) error(std::move(BErr), MachO.getFileName()); // Set the symbol names and indirect names for the added symbols. if (BindsAdded) { BOS.flush(); const char *Q = BindsNameBuffer.c_str(); for (unsigned K = 0; K < BindsAdded; K++) { SymbolList[I].Name = Q; Q += strlen(Q) + 1; if (SymbolList[I].TypeChar == 'I') { SymbolList[I].IndirectName = Q; Q += strlen(Q) + 1; } I++; } } // Add the undefined symbols from the lazy bind entries. unsigned LazysAdded = 0; Error LErr = Error::success(); LastSymbolName = StringRef(); for (const llvm::object::MachOBindEntry &Entry : MachO.lazyBindTable(LErr)) { bool found = false; if (LastSymbolName == Entry.symbolName()) found = true; else { // Here we must check to see it this symbol is already in the // SymbolList as it might have already have been added above via a // non-lazy (bind) entry. for (unsigned J = 0; J < SymbolList.size() && !found; ++J) { if (SymbolList[J].Name == Entry.symbolName()) found = true; } } if (!found) { LastSymbolName = Entry.symbolName(); NMSymbol L = {}; L.Name = Entry.symbolName().str(); L.Address = 0; L.Size = 0; L.TypeChar = 'U'; // There is no symbol in the nlist symbol table for this so we set // Sym effectivly to null and the rest of code in here must test for // it and not do things like Sym.getFlags() for it. L.Sym = BasicSymbolRef(); L.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined; L.NType = MachO::N_EXT | MachO::N_UNDF; L.NSect = 0; // The REFERENCE_FLAG_UNDEFINED_LAZY is no longer used but here it // makes sence since we are creating this from a lazy bind entry. L.NDesc = MachO::REFERENCE_FLAG_UNDEFINED_LAZY; MachO::SET_LIBRARY_ORDINAL(L.NDesc, Entry.ordinal()); SymbolList.push_back(L); LOS << Entry.symbolName(); LOS << '\0'; LazysAdded++; } } if (LErr) error(std::move(LErr), MachO.getFileName()); // Set the symbol names and indirect names for the added symbols. if (LazysAdded) { LOS.flush(); const char *Q = LazysNameBuffer.c_str(); for (unsigned K = 0; K < LazysAdded; K++) { SymbolList[I].Name = Q; Q += strlen(Q) + 1; if (SymbolList[I].TypeChar == 'I') { SymbolList[I].IndirectName = Q; Q += strlen(Q) + 1; } I++; } } // Add the undefineds symbol from the weak bind entries which are not // strong symbols. unsigned WeaksAdded = 0; Error WErr = Error::success(); LastSymbolName = StringRef(); for (const llvm::object::MachOBindEntry &Entry : MachO.weakBindTable(WErr)) { bool found = false; unsigned J = 0; if (LastSymbolName == Entry.symbolName() || Entry.flags() & MachO::BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) { found = true; } else { for (J = 0; J < SymbolList.size() && !found; ++J) { if (SymbolList[J].Name == Entry.symbolName()) { found = true; break; } } } if (!found) { LastSymbolName = Entry.symbolName(); NMSymbol W = {}; W.Name = Entry.symbolName().str(); W.Address = 0; W.Size = 0; W.TypeChar = 'U'; // There is no symbol in the nlist symbol table for this so we set // Sym effectivly to null and the rest of code in here must test for // it and not do things like Sym.getFlags() for it. W.Sym = BasicSymbolRef(); W.SymFlags = SymbolRef::SF_Global | SymbolRef::SF_Undefined; W.NType = MachO::N_EXT | MachO::N_UNDF; W.NSect = 0; // Odd that we are using N_WEAK_DEF on an undefined symbol but that is // what is created in this case by the linker when there are real // symbols in the nlist structs. W.NDesc = MachO::N_WEAK_DEF; SymbolList.push_back(W); WOS << Entry.symbolName(); WOS << '\0'; WeaksAdded++; } else { // This is the case the symbol was previously been found and it could // have been added from a bind or lazy bind symbol. If so and not // a definition also mark it as weak. if (SymbolList[J].TypeChar == 'U') // See comment above about N_WEAK_DEF. SymbolList[J].NDesc |= MachO::N_WEAK_DEF; } } if (WErr) error(std::move(WErr), MachO.getFileName()); // Set the symbol names and indirect names for the added symbols. if (WeaksAdded) { WOS.flush(); const char *Q = WeaksNameBuffer.c_str(); for (unsigned K = 0; K < WeaksAdded; K++) { SymbolList[I].Name = Q; Q += strlen(Q) + 1; if (SymbolList[I].TypeChar == 'I') { SymbolList[I].IndirectName = Q; Q += strlen(Q) + 1; } I++; } } // Trying adding symbol from the function starts table and LC_MAIN entry // point. SmallVector FoundFns; uint64_t lc_main_offset = UINT64_MAX; for (const auto &Command : MachO.load_commands()) { if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) { // We found a function starts segment, parse the addresses for // consumption. MachO::linkedit_data_command LLC = MachO.getLinkeditDataLoadCommand(Command); MachO.ReadULEB128s(LLC.dataoff, FoundFns); } else if (Command.C.cmd == MachO::LC_MAIN) { MachO::entry_point_command LCmain = MachO.getEntryPointCommand(Command); lc_main_offset = LCmain.entryoff; } } // See if these addresses are already in the symbol table. unsigned FunctionStartsAdded = 0; for (uint64_t f = 0; f < FoundFns.size(); f++) { bool found = false; for (unsigned J = 0; J < SymbolList.size() && !found; ++J) { if (SymbolList[J].Address == FoundFns[f] + BaseSegmentAddress) found = true; } // See this address is not already in the symbol table fake up an // nlist for it. if (!found) { NMSymbol F = {}; F.Name = ""; F.Address = FoundFns[f] + BaseSegmentAddress; F.Size = 0; // There is no symbol in the nlist symbol table for this so we set // Sym effectivly to null and the rest of code in here must test for // it and not do things like Sym.getFlags() for it. F.Sym = BasicSymbolRef(); F.SymFlags = 0; F.NType = MachO::N_SECT; F.NSect = 0; StringRef SegmentName = StringRef(); StringRef SectionName = StringRef(); for (const SectionRef &Section : MachO.sections()) { if (Expected NameOrErr = Section.getName()) SectionName = *NameOrErr; else consumeError(NameOrErr.takeError()); SegmentName = MachO.getSectionFinalSegmentName(Section.getRawDataRefImpl()); F.NSect++; if (F.Address >= Section.getAddress() && F.Address < Section.getAddress() + Section.getSize()) { F.Section = Section; break; } } if (SegmentName == "__TEXT" && SectionName == "__text") F.TypeChar = 't'; else if (SegmentName == "__DATA" && SectionName == "__data") F.TypeChar = 'd'; else if (SegmentName == "__DATA" && SectionName == "__bss") F.TypeChar = 'b'; else F.TypeChar = 's'; F.NDesc = 0; SymbolList.push_back(F); if (FoundFns[f] == lc_main_offset) FOS << ""; else FOS << ""; FOS << '\0'; FunctionStartsAdded++; } } if (FunctionStartsAdded) { FOS.flush(); const char *Q = FunctionStartsNameBuffer.c_str(); for (unsigned K = 0; K < FunctionStartsAdded; K++) { SymbolList[I].Name = Q; Q += strlen(Q) + 1; if (SymbolList[I].TypeChar == 'I') { SymbolList[I].IndirectName = Q; Q += strlen(Q) + 1; } I++; } } } } static bool shouldDump(SymbolicFile &Obj) { // The -X option is currently only implemented for XCOFF, ELF, and IR object // files. The option isn't fundamentally impossible with other formats, just // isn't implemented. if (!isa(Obj) && !isa(Obj) && !isa(Obj)) return true; return Obj.is64Bit() ? BitMode != BitModeTy::Bit32 : BitMode != BitModeTy::Bit64; } static void getXCOFFExports(XCOFFObjectFile *XCOFFObj, std::vector &SymbolList, StringRef ArchiveName) { // Skip Shared object file. if (XCOFFObj->getFlags() & XCOFF::F_SHROBJ) return; for (SymbolRef Sym : XCOFFObj->symbols()) { // There is no visibility in old 32 bit XCOFF object file interpret. bool HasVisibilityAttr = XCOFFObj->is64Bit() || (XCOFFObj->auxiliaryHeader32() && (XCOFFObj->auxiliaryHeader32()->getVersion() == XCOFF::NEW_XCOFF_INTERPRET)); if (HasVisibilityAttr) { XCOFFSymbolRef XCOFFSym = XCOFFObj->toSymbolRef(Sym.getRawDataRefImpl()); uint16_t SymType = XCOFFSym.getSymbolType(); if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_INTERNAL) continue; if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_HIDDEN) continue; } Expected SymSecOrErr = Sym.getSection(); if (!SymSecOrErr) { warn(SymSecOrErr.takeError(), XCOFFObj->getFileName(), "for symbol with index " + Twine(XCOFFObj->getSymbolIndex(Sym.getRawDataRefImpl().p)), ArchiveName); continue; } section_iterator SecIter = *SymSecOrErr; // If the symbol is not in a text or data section, it is not exported. if (SecIter == XCOFFObj->section_end()) continue; if (!(SecIter->isText() || SecIter->isData() || SecIter->isBSS())) continue; StringRef SymName = cantFail(Sym.getName()); if (SymName.empty()) continue; if (SymName.starts_with("__sinit") || SymName.starts_with("__sterm") || SymName.front() == '.' || SymName.front() == '(') continue; // Check the SymName regex matching with "^__[0-9]+__". if (SymName.size() > 4 && SymName.starts_with("__") && SymName.ends_with("__")) { if (std::all_of(SymName.begin() + 2, SymName.end() - 2, isDigit)) continue; } if (SymName == "__rsrc" && NoRsrc) continue; if (SymName.starts_with("__tf1")) SymName = SymName.substr(6); else if (SymName.starts_with("__tf9")) SymName = SymName.substr(14); NMSymbol S = {}; S.Name = SymName.str(); S.Sym = Sym; if (HasVisibilityAttr) { XCOFFSymbolRef XCOFFSym = XCOFFObj->toSymbolRef(Sym.getRawDataRefImpl()); uint16_t SymType = XCOFFSym.getSymbolType(); if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_PROTECTED) S.Visibility = "protected"; else if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_EXPORTED) S.Visibility = "export"; } if (S.initializeFlags(*XCOFFObj)) SymbolList.push_back(S); } } static Expected getDynamicSyms(SymbolicFile &Obj) { const auto *E = dyn_cast(&Obj); if (!E) return createError("File format has no dynamic symbol table"); return E->getDynamicSymbolIterators(); } // Returns false if there is error found or true otherwise. static bool getSymbolNamesFromObject(SymbolicFile &Obj, std::vector &SymbolList) { auto Symbols = Obj.symbols(); std::vector SymbolVersions; if (DynamicSyms) { Expected SymbolsOrErr = getDynamicSyms(Obj); if (!SymbolsOrErr) { error(SymbolsOrErr.takeError(), Obj.getFileName()); return false; } Symbols = *SymbolsOrErr; if (const auto *E = dyn_cast(&Obj)) { if (Expected> VersionsOrErr = E->readDynsymVersions()) SymbolVersions = std::move(*VersionsOrErr); else WithColor::warning(errs(), ToolName) << "unable to read symbol versions: " << toString(VersionsOrErr.takeError()) << "\n"; } } // If a "-s segname sectname" option was specified and this is a Mach-O // file get the section number for that section in this object file. unsigned int Nsect = 0; MachOObjectFile *MachO = dyn_cast(&Obj); if (!SegSect.empty() && MachO) { Nsect = getNsectForSegSect(MachO); // If this section is not in the object file no symbols are printed. if (Nsect == 0) return false; } if (!(MachO && DyldInfoOnly)) { size_t I = -1; for (BasicSymbolRef Sym : Symbols) { ++I; Expected SymFlagsOrErr = Sym.getFlags(); if (!SymFlagsOrErr) { error(SymFlagsOrErr.takeError(), Obj.getFileName()); return false; } // Don't drop format specifc symbols for ARM and AArch64 ELF targets, they // are used to repesent mapping symbols and needed to honor the // --special-syms option. auto *ELFObj = dyn_cast(&Obj); bool HasMappingSymbol = ELFObj && llvm::is_contained({ELF::EM_ARM, ELF::EM_AARCH64, ELF::EM_CSKY, ELF::EM_RISCV}, ELFObj->getEMachine()); if (!HasMappingSymbol && !DebugSyms && (*SymFlagsOrErr & SymbolRef::SF_FormatSpecific)) continue; if (WithoutAliases && (*SymFlagsOrErr & SymbolRef::SF_Indirect)) continue; // If a "-s segname sectname" option was specified and this is a Mach-O // file and this section appears in this file, Nsect will be non-zero then // see if this symbol is a symbol from that section and if not skip it. if (Nsect && Nsect != getNsectInMachO(*MachO, Sym)) continue; NMSymbol S = {}; S.Size = 0; S.Address = 0; if (isa(&Obj)) S.Size = ELFSymbolRef(Sym).getSize(); if (const XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj)) S.Size = XCOFFObj->getSymbolSize(Sym.getRawDataRefImpl()); if (const WasmObjectFile *WasmObj = dyn_cast(&Obj)) S.Size = WasmObj->getSymbolSize(Sym); if (PrintAddress && isa(Obj)) { SymbolRef SymRef(Sym); Expected AddressOrErr = SymRef.getAddress(); if (!AddressOrErr) { consumeError(AddressOrErr.takeError()); break; } S.Address = *AddressOrErr; } S.TypeName = getNMTypeName(Obj, Sym); S.TypeChar = getNMSectionTagAndName(Obj, Sym, S.SectionName); raw_string_ostream OS(S.Name); if (Error E = Sym.printName(OS)) { if (MachO) { OS << "bad string index"; consumeError(std::move(E)); } else error(std::move(E), Obj.getFileName()); } if (!SymbolVersions.empty() && !SymbolVersions[I].Name.empty()) S.Name += (SymbolVersions[I].IsVerDef ? "@@" : "@") + SymbolVersions[I].Name; S.Sym = Sym; if (S.initializeFlags(Obj)) SymbolList.push_back(S); } } // If this is a Mach-O file where the nlist symbol table is out of sync // with the dyld export trie then look through exports and fake up symbols // for the ones that are missing (also done with the -add-dyldinfo flag). // This is needed if strip(1) -T is run on a binary containing swift // language symbols for example. The option -only-dyldinfo will fake up // all symbols from the dyld export trie as well as the bind info. if (MachO && !NoDyldInfo) dumpSymbolsFromDLInfoMachO(*MachO, SymbolList); return true; } static void printObjectLabel(bool PrintArchiveName, StringRef ArchiveName, StringRef ArchitectureName, StringRef ObjectFileName) { outs() << "\n"; if (ArchiveName.empty() || !PrintArchiveName) outs() << ObjectFileName; else outs() << ArchiveName << "(" << ObjectFileName << ")"; if (!ArchitectureName.empty()) outs() << " (for architecture " << ArchitectureName << ")"; outs() << ":\n"; } static Expected hasSymbols(SymbolicFile &Obj) { if (DynamicSyms) { Expected DynamicSymsOrErr = getDynamicSyms(Obj); if (!DynamicSymsOrErr) return DynamicSymsOrErr.takeError(); return !DynamicSymsOrErr->empty(); } return !Obj.symbols().empty(); } static void printSymbolNamesFromObject( SymbolicFile &Obj, std::vector &SymbolList, bool PrintSymbolObject, bool PrintObjectLabel, StringRef ArchiveName = {}, StringRef ArchitectureName = {}, StringRef ObjectName = {}, bool PrintArchiveName = true) { if (PrintObjectLabel && !ExportSymbols) printObjectLabel(PrintArchiveName, ArchiveName, ArchitectureName, ObjectName.empty() ? Obj.getFileName() : ObjectName); if (!getSymbolNamesFromObject(Obj, SymbolList) || ExportSymbols) return; // If there is an error in hasSymbols(), the error should be encountered in // function getSymbolNamesFromObject first. if (!cantFail(hasSymbols(Obj)) && SymbolList.empty() && !Quiet) { writeFileName(errs(), ArchiveName, ArchitectureName); errs() << "no symbols\n"; } sortSymbolList(SymbolList); printSymbolList(Obj, SymbolList, PrintSymbolObject, ArchiveName, ArchitectureName); } static void dumpSymbolsNameFromMachOFilesetEntry( MachOObjectFile *Obj, std::vector &SymbolList, bool PrintSymbolObject, bool PrintObjectLabel) { auto Buf = Obj->getMemoryBufferRef(); const auto *End = Obj->load_commands().end(); for (const auto *It = Obj->load_commands().begin(); It != End; ++It) { const auto &Command = *It; if (Command.C.cmd != MachO::LC_FILESET_ENTRY) continue; MachO::fileset_entry_command Entry = Obj->getFilesetEntryLoadCommand(Command); auto MaybeMachO = MachOObjectFile::createMachOObjectFile(Buf, 0, 0, Entry.fileoff); if (Error Err = MaybeMachO.takeError()) report_fatal_error(std::move(Err)); const char *EntryName = Command.Ptr + Entry.entry_id.offset; if (EntryName) outs() << "Symbols for " << EntryName << ": \n"; std::unique_ptr EntryMachO = std::move(MaybeMachO.get()); printSymbolNamesFromObject(*EntryMachO, SymbolList, PrintSymbolObject, PrintObjectLabel); if (std::next(It) != End) outs() << "\n"; } } static void dumpSymbolNamesFromObject( SymbolicFile &Obj, std::vector &SymbolList, bool PrintSymbolObject, bool PrintObjectLabel, StringRef ArchiveName = {}, StringRef ArchitectureName = {}, StringRef ObjectName = {}, bool PrintArchiveName = true) { if (!shouldDump(Obj)) return; if (ExportSymbols && Obj.isXCOFF()) { XCOFFObjectFile *XCOFFObj = cast(&Obj); getXCOFFExports(XCOFFObj, SymbolList, ArchiveName); return; } CurrentFilename = Obj.getFileName(); // Are we handling a MachO of type MH_FILESET? if (Obj.isMachO() && Obj.is64Bit() && cast(&Obj)->getHeader64().filetype == MachO::MH_FILESET) { dumpSymbolsNameFromMachOFilesetEntry(cast(&Obj), SymbolList, PrintSymbolObject, PrintObjectLabel); return; } printSymbolNamesFromObject(Obj, SymbolList, PrintSymbolObject, PrintObjectLabel, ArchiveName, ArchitectureName, ObjectName, PrintArchiveName); } // checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file // and if it is and there is a list of architecture flags is specified then // check to make sure this Mach-O file is one of those architectures or all // architectures was specificed. If not then an error is generated and this // routine returns false. Else it returns true. static bool checkMachOAndArchFlags(SymbolicFile *O, StringRef Filename) { auto *MachO = dyn_cast(O); if (!MachO || ArchAll || ArchFlags.empty()) return true; MachO::mach_header H; MachO::mach_header_64 H_64; Triple T; const char *McpuDefault, *ArchFlag; if (MachO->is64Bit()) { H_64 = MachO->MachOObjectFile::getHeader64(); T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype, &McpuDefault, &ArchFlag); } else { H = MachO->MachOObjectFile::getHeader(); T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype, &McpuDefault, &ArchFlag); } const std::string ArchFlagName(ArchFlag); if (!llvm::is_contained(ArchFlags, ArchFlagName)) { error("No architecture specified", Filename); return false; } return true; } static void printArchiveMap(iterator_range &map, StringRef Filename) { for (auto I : map) { Expected C = I.getMember(); if (!C) { error(C.takeError(), Filename); break; } Expected FileNameOrErr = C->getName(); if (!FileNameOrErr) { error(FileNameOrErr.takeError(), Filename); break; } StringRef SymName = I.getName(); outs() << SymName << " in " << FileNameOrErr.get() << "\n"; } outs() << "\n"; } static void dumpArchiveMap(Archive *A, StringRef Filename) { auto Map = A->symbols(); if (!Map.empty()) { outs() << "Archive map\n"; printArchiveMap(Map, Filename); } auto ECMap = A->ec_symbols(); if (!ECMap) { warn(ECMap.takeError(), Filename); } else if (!ECMap->empty()) { outs() << "Archive EC map\n"; printArchiveMap(*ECMap, Filename); } } static void dumpArchive(Archive *A, std::vector &SymbolList, StringRef Filename, LLVMContext *ContextPtr) { if (ArchiveMap) dumpArchiveMap(A, Filename); Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected> ChildOrErr = C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) error(std::move(E), Filename, C); continue; } if (SymbolicFile *O = dyn_cast(&*ChildOrErr.get())) { if (!MachOPrintSizeWarning && PrintSize && isa(O)) { WithColor::warning(errs(), ToolName) << "sizes with -print-size for Mach-O files are always zero.\n"; MachOPrintSizeWarning = true; } if (!checkMachOAndArchFlags(O, Filename)) return; dumpSymbolNamesFromObject(*O, SymbolList, /*PrintSymbolObject=*/false, !PrintFileName, Filename, /*ArchitectureName=*/{}, O->getFileName(), /*PrintArchiveName=*/false); } } if (Err) error(std::move(Err), A->getFileName()); } static void dumpMachOUniversalBinaryMatchArchFlags( MachOUniversalBinary *UB, std::vector &SymbolList, StringRef Filename, LLVMContext *ContextPtr) { // Look for a slice in the universal binary that matches each ArchFlag. bool ArchFound; for (unsigned i = 0; i < ArchFlags.size(); ++i) { ArchFound = false; for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { if (ArchFlags[i] == I->getArchFlagName()) { ArchFound = true; Expected> ObjOrErr = I->getAsObjectFile(); std::string ArchiveName; std::string ArchitectureName; ArchiveName.clear(); ArchitectureName.clear(); if (ObjOrErr) { ObjectFile &Obj = *ObjOrErr.get(); if (ArchFlags.size() > 1) ArchitectureName = I->getArchFlagName(); dumpSymbolNamesFromObject(Obj, SymbolList, /*PrintSymbolObject=*/false, (ArchFlags.size() > 1) && !PrintFileName, ArchiveName, ArchitectureName); } else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { error(std::move(E), Filename, ArchFlags.size() > 1 ? StringRef(I->getArchFlagName()) : StringRef()); continue; } else if (Expected> AOrErr = I->getAsArchive()) { std::unique_ptr &A = *AOrErr; Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected> ChildOrErr = C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { error(std::move(E), Filename, C, ArchFlags.size() > 1 ? StringRef(I->getArchFlagName()) : StringRef()); } continue; } if (SymbolicFile *O = dyn_cast(&*ChildOrErr.get())) { ArchiveName = std::string(A->getFileName()); if (ArchFlags.size() > 1) ArchitectureName = I->getArchFlagName(); dumpSymbolNamesFromObject( *O, SymbolList, /*PrintSymbolObject=*/false, !PrintFileName, ArchiveName, ArchitectureName); } } if (Err) error(std::move(Err), A->getFileName()); } else { consumeError(AOrErr.takeError()); error(Filename + " for architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file", "Mach-O universal file"); } } } if (!ArchFound) { error(ArchFlags[i], "file: " + Filename + " does not contain architecture"); return; } } } // Returns true If the binary contains a slice that matches the host // architecture, or false otherwise. static bool dumpMachOUniversalBinaryMatchHost(MachOUniversalBinary *UB, std::vector &SymbolList, StringRef Filename, LLVMContext *ContextPtr) { Triple HostTriple = MachOObjectFile::getHostArch(); StringRef HostArchName = HostTriple.getArchName(); for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { if (HostArchName == I->getArchFlagName()) { Expected> ObjOrErr = I->getAsObjectFile(); std::string ArchiveName; if (ObjOrErr) { ObjectFile &Obj = *ObjOrErr.get(); dumpSymbolNamesFromObject(Obj, SymbolList, /*PrintSymbolObject=*/false, /*PrintObjectLabel=*/false); } else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) error(std::move(E), Filename); else if (Expected> AOrErr = I->getAsArchive()) { std::unique_ptr &A = *AOrErr; Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected> ChildOrErr = C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) error(std::move(E), Filename, C); continue; } if (SymbolicFile *O = dyn_cast(&*ChildOrErr.get())) { ArchiveName = std::string(A->getFileName()); dumpSymbolNamesFromObject(*O, SymbolList, /*PrintSymbolObject=*/false, !PrintFileName, ArchiveName); } } if (Err) error(std::move(Err), A->getFileName()); } else { consumeError(AOrErr.takeError()); error(Filename + " for architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file", "Mach-O universal file"); } return true; } } return false; } static void dumpMachOUniversalBinaryArchAll(MachOUniversalBinary *UB, std::vector &SymbolList, StringRef Filename, LLVMContext *ContextPtr) { bool moreThanOneArch = UB->getNumberOfObjects() > 1; for (const MachOUniversalBinary::ObjectForArch &O : UB->objects()) { Expected> ObjOrErr = O.getAsObjectFile(); std::string ArchiveName; std::string ArchitectureName; ArchiveName.clear(); ArchitectureName.clear(); if (ObjOrErr) { ObjectFile &Obj = *ObjOrErr.get(); if (isa(Obj) && moreThanOneArch) ArchitectureName = O.getArchFlagName(); dumpSymbolNamesFromObject(Obj, SymbolList, /*PrintSymbolObject=*/false, !PrintFileName, ArchiveName, ArchitectureName); } else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { error(std::move(E), Filename, moreThanOneArch ? StringRef(O.getArchFlagName()) : StringRef()); continue; } else if (Expected> AOrErr = O.getAsArchive()) { std::unique_ptr &A = *AOrErr; Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected> ChildOrErr = C.getAsBinary(ContextPtr); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) error(std::move(E), Filename, C, moreThanOneArch ? StringRef(ArchitectureName) : StringRef()); continue; } if (SymbolicFile *F = dyn_cast(&*ChildOrErr.get())) { ArchiveName = std::string(A->getFileName()); if (isa(F) && moreThanOneArch) ArchitectureName = O.getArchFlagName(); dumpSymbolNamesFromObject(*F, SymbolList, /*PrintSymbolObject=*/false, !PrintFileName, ArchiveName, ArchitectureName); } } if (Err) error(std::move(Err), A->getFileName()); } else { consumeError(AOrErr.takeError()); error(Filename + " for architecture " + StringRef(O.getArchFlagName()) + " is not a Mach-O file or an archive file", "Mach-O universal file"); } } } static void dumpMachOUniversalBinary(MachOUniversalBinary *UB, std::vector &SymbolList, StringRef Filename, LLVMContext *ContextPtr) { // If we have a list of architecture flags specified dump only those. if (!ArchAll && !ArchFlags.empty()) { dumpMachOUniversalBinaryMatchArchFlags(UB, SymbolList, Filename, ContextPtr); return; } // No architecture flags were specified so if this contains a slice that // matches the host architecture dump only that. if (!ArchAll && dumpMachOUniversalBinaryMatchHost(UB, SymbolList, Filename, ContextPtr)) return; // Either all architectures have been specified or none have been specified // and this does not contain the host architecture so dump all the slices. dumpMachOUniversalBinaryArchAll(UB, SymbolList, Filename, ContextPtr); } static void dumpTapiUniversal(TapiUniversal *TU, std::vector &SymbolList, StringRef Filename) { for (const TapiUniversal::ObjectForArch &I : TU->objects()) { StringRef ArchName = I.getArchFlagName(); const bool ShowArch = ArchFlags.empty() || llvm::is_contained(ArchFlags, ArchName); if (!ShowArch) continue; if (!AddInlinedInfo && !I.isTopLevelLib()) continue; if (auto ObjOrErr = I.getAsObjectFile()) dumpSymbolNamesFromObject( *ObjOrErr.get(), SymbolList, /*PrintSymbolObject=*/false, /*PrintObjectLabel=*/true, /*ArchiveName=*/{}, ArchName, I.getInstallName()); else if (Error E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) { error(std::move(E), Filename, ArchName); } } } static void dumpSymbolicFile(SymbolicFile *O, std::vector &SymbolList, StringRef Filename) { if (!MachOPrintSizeWarning && PrintSize && isa(O)) { WithColor::warning(errs(), ToolName) << "sizes with --print-size for Mach-O files are always zero.\n"; MachOPrintSizeWarning = true; } if (!checkMachOAndArchFlags(O, Filename)) return; dumpSymbolNamesFromObject(*O, SymbolList, /*PrintSymbolObject=*/true, /*PrintObjectLabel=*/false); } static std::vector dumpSymbolNamesFromFile(StringRef Filename) { std::vector SymbolList; ErrorOr> BufferOrErr = MemoryBuffer::getFileOrSTDIN(Filename); if (error(BufferOrErr.getError(), Filename)) return SymbolList; // Ignore AIX linker import files (these files start with "#!"), when // exporting symbols. const char *BuffStart = (*BufferOrErr)->getBufferStart(); size_t BufferSize = (*BufferOrErr)->getBufferSize(); if (ExportSymbols && BufferSize >= 2 && BuffStart[0] == '#' && BuffStart[1] == '!') return SymbolList; LLVMContext Context; LLVMContext *ContextPtr = NoLLVMBitcode ? nullptr : &Context; Expected> BinaryOrErr = createBinary(BufferOrErr.get()->getMemBufferRef(), ContextPtr); if (!BinaryOrErr) { error(BinaryOrErr.takeError(), Filename); return SymbolList; } Binary &Bin = *BinaryOrErr.get(); if (Archive *A = dyn_cast(&Bin)) dumpArchive(A, SymbolList, Filename, ContextPtr); else if (MachOUniversalBinary *UB = dyn_cast(&Bin)) dumpMachOUniversalBinary(UB, SymbolList, Filename, ContextPtr); else if (TapiUniversal *TU = dyn_cast(&Bin)) dumpTapiUniversal(TU, SymbolList, Filename); else if (SymbolicFile *O = dyn_cast(&Bin)) dumpSymbolicFile(O, SymbolList, Filename); return SymbolList; } static void exportSymbolNamesFromFiles(const std::vector &InputFilenames) { std::vector SymbolList; for (const auto &FileName : InputFilenames) { std::vector FileSymList = dumpSymbolNamesFromFile(FileName); SymbolList.insert(SymbolList.end(), FileSymList.begin(), FileSymList.end()); } // Delete symbols which should not be printed from SymolList. llvm::erase_if(SymbolList, [](const NMSymbol &s) { return !s.shouldPrint(); }); sortSymbolList(SymbolList); SymbolList.erase(std::unique(SymbolList.begin(), SymbolList.end()), SymbolList.end()); printExportSymbolList(SymbolList); } int llvm_nm_main(int argc, char **argv, const llvm::ToolContext &) { BumpPtrAllocator A; StringSaver Saver(A); NmOptTable Tbl; ToolName = argv[0]; opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { error(Msg); exit(1); }); if (Args.hasArg(OPT_help)) { Tbl.printHelp( outs(), (Twine(ToolName) + " [options] ").str().c_str(), "LLVM symbol table dumper"); // TODO Replace this with OptTable API once it adds extrahelp support. outs() << "\nPass @FILE as argument to read options from FILE.\n"; return 0; } if (Args.hasArg(OPT_version)) { // This needs to contain the word "GNU", libtool looks for that string. outs() << "llvm-nm, compatible with GNU nm" << '\n'; cl::PrintVersionMessage(); return 0; } DebugSyms = Args.hasArg(OPT_debug_syms); DefinedOnly = Args.hasArg(OPT_defined_only); Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, false); DynamicSyms = Args.hasArg(OPT_dynamic); ExternalOnly = Args.hasArg(OPT_extern_only); StringRef V = Args.getLastArgValue(OPT_format_EQ, "bsd"); if (V == "bsd") OutputFormat = bsd; else if (V == "posix") OutputFormat = posix; else if (V == "sysv") OutputFormat = sysv; else if (V == "darwin") OutputFormat = darwin; else if (V == "just-symbols") OutputFormat = just_symbols; else error("--format value should be one of: bsd, posix, sysv, darwin, " "just-symbols"); LineNumbers = Args.hasArg(OPT_line_numbers); NoLLVMBitcode = Args.hasArg(OPT_no_llvm_bc); NoSort = Args.hasArg(OPT_no_sort); NoWeakSymbols = Args.hasArg(OPT_no_weak); NumericSort = Args.hasArg(OPT_numeric_sort); ArchiveMap = Args.hasArg(OPT_print_armap); PrintFileName = Args.hasArg(OPT_print_file_name); PrintSize = Args.hasArg(OPT_print_size); ReverseSort = Args.hasArg(OPT_reverse_sort); ExportSymbols = Args.hasArg(OPT_export_symbols); if (ExportSymbols) { ExternalOnly = true; DefinedOnly = true; } Quiet = Args.hasArg(OPT_quiet); V = Args.getLastArgValue(OPT_radix_EQ, "x"); if (V == "o") AddressRadix = Radix::o; else if (V == "d") AddressRadix = Radix::d; else if (V == "x") AddressRadix = Radix::x; else error("--radix value should be one of: 'o' (octal), 'd' (decimal), 'x' " "(hexadecimal)"); SizeSort = Args.hasArg(OPT_size_sort); SpecialSyms = Args.hasArg(OPT_special_syms); UndefinedOnly = Args.hasArg(OPT_undefined_only); WithoutAliases = Args.hasArg(OPT_without_aliases); // Get BitMode from enviornment variable "OBJECT_MODE" for AIX OS, if // specified. Triple HostTriple(sys::getProcessTriple()); if (HostTriple.isOSAIX()) { BitMode = StringSwitch(getenv("OBJECT_MODE")) .Case("32", BitModeTy::Bit32) .Case("64", BitModeTy::Bit64) .Case("32_64", BitModeTy::Bit32_64) .Case("any", BitModeTy::Any) .Default(BitModeTy::Bit32); } else BitMode = BitModeTy::Any; if (Arg *A = Args.getLastArg(OPT_X)) { StringRef Mode = A->getValue(); if (Mode == "32") BitMode = BitModeTy::Bit32; else if (Mode == "64") BitMode = BitModeTy::Bit64; else if (Mode == "32_64") BitMode = BitModeTy::Bit32_64; else if (Mode == "any") BitMode = BitModeTy::Any; else error("-X value should be one of: 32, 64, 32_64, (default) any"); } // Mach-O specific options. FormatMachOasHex = Args.hasArg(OPT_x); AddDyldInfo = Args.hasArg(OPT_add_dyldinfo); AddInlinedInfo = Args.hasArg(OPT_add_inlinedinfo); DyldInfoOnly = Args.hasArg(OPT_dyldinfo_only); NoDyldInfo = Args.hasArg(OPT_no_dyldinfo); // XCOFF specific options. NoRsrc = Args.hasArg(OPT_no_rsrc); // llvm-nm only reads binary files. if (error(sys::ChangeStdinToBinary())) return 1; // These calls are needed so that we can read bitcode correctly. llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); // The relative order of these is important. If you pass --size-sort it should // only print out the size. However, if you pass -S --size-sort, it should // print out both the size and address. if (SizeSort && !PrintSize) PrintAddress = false; if (OutputFormat == sysv || SizeSort) PrintSize = true; for (const auto *A : Args.filtered(OPT_arch_EQ)) { SmallVector Values; llvm::SplitString(A->getValue(), Values, ","); for (StringRef V : Values) { if (V == "all") ArchAll = true; else if (MachOObjectFile::isValidArch(V)) ArchFlags.push_back(V); else error("Unknown architecture named '" + V + "'", "for the --arch option"); } } // Mach-O takes -s to accept two arguments. We emulate this by iterating over // both OPT_s and OPT_INPUT. std::vector InputFilenames; int SegSectArgs = 0; for (opt::Arg *A : Args.filtered(OPT_s, OPT_INPUT)) { if (SegSectArgs > 0) { --SegSectArgs; SegSect.push_back(A->getValue()); } else if (A->getOption().matches(OPT_s)) { SegSectArgs = 2; } else { InputFilenames.push_back(A->getValue()); } } if (!SegSect.empty() && SegSect.size() != 2) error("bad number of arguments (must be two arguments)", "for the -s option"); if (InputFilenames.empty()) InputFilenames.push_back("a.out"); if (InputFilenames.size() > 1) MultipleFiles = true; if (NoDyldInfo && (AddDyldInfo || DyldInfoOnly)) error("--no-dyldinfo can't be used with --add-dyldinfo or --dyldinfo-only"); if (ExportSymbols) exportSymbolNamesFromFiles(InputFilenames); else llvm::for_each(InputFilenames, dumpSymbolNamesFromFile); if (HadError) return 1; return 0; }