llvm-project/llvm/tools/llvm-nm/llvm-nm.cpp
Cyndy Ishida bc85cf1687 [TextAPI] Add support for TBDv5 Files to nm & tapi-diff
This includes handling of new attributes for symbols & rpath.
In the event that an older format file is compared to tbd_v5, ignore these new attributes.

Reviewed By: ributzka

Differential Revision: https://reviews.llvm.org/D144529
2023-02-22 19:39:26 -08:00

2477 lines
85 KiB
C++

//===-- 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/XCOFF.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/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/InitLLVM.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 <vector>
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(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
OPT_##ID,
#include "Opts.inc"
#undef OPTION
};
#define PREFIX(NAME, VALUE) \
static constexpr StringLiteral NAME##_init[] = VALUE; \
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
std::size(NAME##_init) - 1);
#include "Opts.inc"
#undef PREFIX
static constexpr opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{ \
PREFIX, NAME, HELPTEXT, \
METAVAR, OPT_##ID, opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, \
OPT_##ALIAS, ALIASARGS, VALUES},
#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 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<StringRef> ArchFlags;
static bool AddDyldInfo;
static bool AddInlinedInfo;
static bool DyldInfoOnly;
static bool FormatMachOasHex;
static bool NoDyldInfo;
static std::vector<StringRef> 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<StringRef> 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) {
uint32_t Flags = cantFail(Sym.getFlags());
return !(Flags & SymbolRef::SF_Undefined);
}
return TypeChar != 'U';
}
bool initializeFlags(const SymbolicFile &Obj) {
Expected<uint32_t> 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<MachOObjectFile>(&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<section_iterator> 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<StringRef> 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 << ")";
}
}
}
outs() << "\n";
}
// 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_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 std::optional<std::string> demangle(StringRef Name) {
std::string Demangled;
if (nonMicrosoftDemangle(Name.str().c_str(), Demangled))
return Demangled;
return std::nullopt;
}
static std::optional<std::string> demangleXCOFF(StringRef Name) {
if (Name.empty() || Name[0] != '.')
return demangle(Name);
Name = Name.drop_front();
std::optional<std::string> DemangledName = demangle(Name);
if (DemangledName)
return "." + *DemangledName;
return std::nullopt;
}
static std::optional<std::string> demangleMachO(StringRef Name) {
if (!Name.empty() && Name[0] == '_')
Name = Name.drop_front();
return demangle(Name);
}
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<NMSymbol> &SymbolList) {
if (NoSort)
return;
if (ReverseSort)
llvm::sort(SymbolList, std::greater<>());
else
llvm::sort(SymbolList);
}
static void printExportSymbolList(const std::vector<NMSymbol> &SymbolList) {
for (const NMSymbol &Sym : SymbolList) {
outs() << Sym.Name;
if (!Sym.Visibility.empty())
outs() << ' ' << Sym.Visibility;
outs() << '\n';
}
}
static void printSymbolList(SymbolicFile &Obj,
std::vector<NMSymbol> &SymbolList, bool printName,
StringRef ArchiveName, StringRef ArchitectureName) {
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<MachOObjectFile>(&Obj);
if (Demangle) {
function_ref<std::optional<std::string>(StringRef)> Fn = ::demangle;
if (Obj.isXCOFF())
Fn = demangleXCOFF;
if (Obj.isMachO())
Fn = demangleMachO;
if (std::optional<std::string> Opt = Fn(S.Name))
Name = *Opt;
}
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) << "\n";
} 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 << ")";
}
outs() << "\n";
} else if (OutputFormat == sysv) {
outs() << left_justify(Name, 20) << "|" << SymbolAddrStr << "| "
<< S.TypeChar << " |" << right_justify(S.TypeName, 18) << "|"
<< SymbolSizeStr << "| |" << S.SectionName << "\n";
}
}
SymbolList.clear();
}
static char getSymbolNMTypeChar(ELFObjectFileBase &Obj,
basic_symbol_iterator I) {
// OK, this is ELF
elf_symbol_iterator SymI(I);
Expected<elf_section_iterator> 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).startswith(".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<StringRef> Name = SymI->getName();
if (!Name) {
consumeError(Name.takeError());
return '?';
}
char Ret = StringSwitch<char>(*Name)
.StartsWith(".debug", 'N')
.StartsWith(".sxdata", 'N')
.Default('?');
if (Ret != '?')
return Ret;
uint32_t Characteristics = 0;
if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) {
Expected<section_iterator> 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<StringRef> NameOrErr = Obj.getSectionName(Section))
if (NameOrErr->startswith(".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<uint32_t> 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<section_iterator> 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<StringRef> 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_Data:
return 'd';
case SymbolRef::ST_Function:
return 't';
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<ELFObjectFileBase>(&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<ELFObjectFileBase>(&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<ELFObjectFileBase>(&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<elf_section_iterator> SecIOrErr = SymI->getSection();
if (!SecIOrErr) {
consumeError(SecIOrErr.takeError());
return '?';
}
if (*SecIOrErr == ELFObj->section_end())
return '?';
Expected<StringRef> NameOrErr = (*SecIOrErr)->getName();
if (!NameOrErr) {
consumeError(NameOrErr.takeError());
return '?';
}
SecName = *NameOrErr;
}
}
if (Symflags & object::SymbolRef::SF_Undefined) {
if (isa<MachOObjectFile>(Obj) || !(Symflags & object::SymbolRef::SF_Weak))
return 'U';
return isObject(Obj, I) ? 'v' : 'w';
}
if (isa<ELFObjectFileBase>(&Obj))
if (ELFSymbolRef(*I).getELFType() == ELF::STT_GNU_IFUNC)
return 'i';
if (!isa<MachOObjectFile>(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<IRObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*IR, I);
else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*COFF, I);
else if (XCOFFObjectFile *XCOFF = dyn_cast<XCOFFObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*XCOFF, I);
else if (COFFImportFile *COFFImport = dyn_cast<COFFImportFile>(&Obj))
Ret = getSymbolNMTypeChar(*COFFImport);
else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*MachO, I);
else if (WasmObjectFile *Wasm = dyn_cast<WasmObjectFile>(&Obj))
Ret = getSymbolNMTypeChar(*Wasm, I);
else if (TapiFile *Tapi = dyn_cast<TapiFile>(&Obj))
Ret = getSymbolNMTypeChar(*Tapi, I);
else if (ELFObjectFileBase *ELF = dyn_cast<ELFObjectFileBase>(&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<StringRef> 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<NMSymbol> &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<StringRef> 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<uint64_t, 8> 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 = "<redacted function X>";
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<StringRef> 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 << "<redacted LC_MAIN>";
else
FOS << "<redacted function " << f << ">";
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<XCOFFObjectFile>(Obj) && !isa<ELFObjectFileBase>(Obj) &&
!isa<IRObjectFile>(Obj))
return true;
return Obj.is64Bit() ? BitMode != BitModeTy::Bit32
: BitMode != BitModeTy::Bit64;
}
static void getXCOFFExports(XCOFFObjectFile *XCOFFObj,
std::vector<NMSymbol> &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<section_iterator> 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.startswith("__sinit") || SymName.startswith("__sterm") ||
SymName.front() == '.' || SymName.front() == '(')
continue;
// Check the SymName regex matching with "^__[0-9]+__".
if (SymName.size() > 4 && SymName.startswith("__") &&
SymName.endswith("__")) {
if (std::all_of(SymName.begin() + 2, SymName.end() - 2, isDigit))
continue;
}
if (SymName == "__rsrc" && NoRsrc)
continue;
if (SymName.startswith("__tf1"))
SymName = SymName.substr(6);
else if (SymName.startswith("__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<SymbolicFile::basic_symbol_iterator_range>
getDynamicSyms(SymbolicFile &Obj) {
const auto *E = dyn_cast<ELFObjectFileBase>(&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<NMSymbol> &SymbolList) {
auto Symbols = Obj.symbols();
std::vector<VersionEntry> SymbolVersions;
if (DynamicSyms) {
Expected<SymbolicFile::basic_symbol_iterator_range> SymbolsOrErr =
getDynamicSyms(Obj);
if (!SymbolsOrErr) {
error(SymbolsOrErr.takeError(), Obj.getFileName());
return false;
}
Symbols = *SymbolsOrErr;
if (const auto *E = dyn_cast<ELFObjectFileBase>(&Obj)) {
if (Expected<std::vector<VersionEntry>> 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<MachOObjectFile>(&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<uint32_t> 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<ELFObjectFileBase>(&Obj);
if ((!ELFObj || (ELFObj->getEMachine() != ELF::EM_ARM &&
ELFObj->getEMachine() != ELF::EM_AARCH64)) &&
!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<ELFObjectFileBase>(&Obj))
S.Size = ELFSymbolRef(Sym).getSize();
if (const XCOFFObjectFile *XCOFFObj =
dyn_cast<const XCOFFObjectFile>(&Obj))
S.Size = XCOFFObj->getSymbolSize(Sym.getRawDataRefImpl());
if (PrintAddress && isa<ObjectFile>(Obj)) {
SymbolRef SymRef(Sym);
Expected<uint64_t> 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<bool> hasSymbols(SymbolicFile &Obj) {
if (DynamicSyms) {
Expected<SymbolicFile::basic_symbol_iterator_range> DynamicSymsOrErr =
getDynamicSyms(Obj);
if (!DynamicSymsOrErr)
return DynamicSymsOrErr.takeError();
return !DynamicSymsOrErr->empty();
}
return !Obj.symbols().empty();
}
static void dumpSymbolNamesFromObject(
SymbolicFile &Obj, std::vector<NMSymbol> &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<XCOFFObjectFile>(&Obj);
getXCOFFExports(XCOFFObj, SymbolList, ArchiveName);
return;
}
if (PrintObjectLabel && !ExportSymbols)
printObjectLabel(PrintArchiveName, ArchiveName, ArchitectureName,
ObjectName.empty() ? Obj.getFileName() : ObjectName);
if (!getSymbolNamesFromObject(Obj, SymbolList) || ExportSymbols)
return;
CurrentFilename = Obj.getFileName();
// 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);
}
// 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<MachOObjectFile>(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 dumpArchiveMap(Archive *A, StringRef Filename) {
Archive::symbol_iterator I = A->symbol_begin();
Archive::symbol_iterator E = A->symbol_end();
if (I != E) {
outs() << "Archive map\n";
for (; I != E; ++I) {
Expected<Archive::Child> C = I->getMember();
if (!C) {
error(C.takeError(), Filename);
break;
}
Expected<StringRef> FileNameOrErr = C->getName();
if (!FileNameOrErr) {
error(FileNameOrErr.takeError(), Filename);
break;
}
StringRef SymName = I->getName();
outs() << SymName << " in " << FileNameOrErr.get() << "\n";
}
outs() << "\n";
}
}
static void dumpArchive(Archive *A, std::vector<NMSymbol> &SymbolList,
StringRef Filename, LLVMContext *ContextPtr) {
if (ArchiveMap)
dumpArchiveMap(A, Filename);
Error Err = Error::success();
for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(ContextPtr);
if (!ChildOrErr) {
if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
error(std::move(E), Filename, C);
continue;
}
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) {
if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(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<NMSymbol> &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<std::unique_ptr<ObjectFile>> 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<std::unique_ptr<Archive>> AOrErr =
I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
Error Err = Error::success();
for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> 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<SymbolicFile>(&*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<NMSymbol> &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<std::unique_ptr<ObjectFile>> 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<std::unique_ptr<Archive>> AOrErr = I->getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
Error Err = Error::success();
for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr =
C.getAsBinary(ContextPtr);
if (!ChildOrErr) {
if (auto E =
isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
error(std::move(E), Filename, C);
continue;
}
if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*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<NMSymbol> &SymbolList,
StringRef Filename,
LLVMContext *ContextPtr) {
bool moreThanOneArch = UB->getNumberOfObjects() > 1;
for (const MachOUniversalBinary::ObjectForArch &O : UB->objects()) {
Expected<std::unique_ptr<ObjectFile>> ObjOrErr = O.getAsObjectFile();
std::string ArchiveName;
std::string ArchitectureName;
ArchiveName.clear();
ArchitectureName.clear();
if (ObjOrErr) {
ObjectFile &Obj = *ObjOrErr.get();
if (isa<MachOObjectFile>(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<std::unique_ptr<Archive>> AOrErr = O.getAsArchive()) {
std::unique_ptr<Archive> &A = *AOrErr;
Error Err = Error::success();
for (auto &C : A->children(Err)) {
Expected<std::unique_ptr<Binary>> 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<SymbolicFile>(&*ChildOrErr.get())) {
ArchiveName = std::string(A->getFileName());
if (isa<MachOObjectFile>(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<NMSymbol> &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<NMSymbol> &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<NMSymbol> &SymbolList,
StringRef Filename) {
if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(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<NMSymbol> dumpSymbolNamesFromFile(StringRef Filename) {
std::vector<NMSymbol> SymbolList;
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
MemoryBuffer::getFileOrSTDIN(Filename);
if (error(BufferOrErr.getError(), Filename))
return SymbolList;
// Always enable opaque pointers, to handle archives with mixed typed and
// opaque pointer bitcode files gracefully. As we're only reading symbols,
// the used pointer types don't matter.
LLVMContext Context;
Context.setOpaquePointers(true);
LLVMContext *ContextPtr = NoLLVMBitcode ? nullptr : &Context;
Expected<std::unique_ptr<Binary>> BinaryOrErr =
createBinary(BufferOrErr.get()->getMemBufferRef(), ContextPtr);
if (!BinaryOrErr) {
error(BinaryOrErr.takeError(), Filename);
return SymbolList;
}
Binary &Bin = *BinaryOrErr.get();
if (Archive *A = dyn_cast<Archive>(&Bin))
dumpArchive(A, SymbolList, Filename, ContextPtr);
else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin))
dumpMachOUniversalBinary(UB, SymbolList, Filename, ContextPtr);
else if (TapiUniversal *TU = dyn_cast<TapiUniversal>(&Bin))
dumpTapiUniversal(TU, SymbolList, Filename);
else if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin))
dumpSymbolicFile(O, SymbolList, Filename);
return SymbolList;
}
static void
exportSymbolNamesFromFiles(const std::vector<std::string> &InputFilenames) {
std::vector<NMSymbol> SymbolList;
for (const auto &FileName : InputFilenames) {
std::vector<NMSymbol> 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 &) {
InitLLVM X(argc, argv);
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] <input object files>").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");
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<BitModeTy>(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<StringRef, 2> 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<std::string> 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;
}