diff --git a/llvm/docs/CommandGuide/llvm-nm.rst b/llvm/docs/CommandGuide/llvm-nm.rst --- a/llvm/docs/CommandGuide/llvm-nm.rst +++ b/llvm/docs/CommandGuide/llvm-nm.rst @@ -142,6 +142,11 @@ Display dynamic symbols instead of normal symbols. +.. option:: --export-symbols + + Print sorted symbols with their visibility (if applicable), with duplicates + removed. + .. option:: --extern-only, -g Print only symbols whose definitions are external; that is, accessible from @@ -267,6 +272,13 @@ Print symbol entry in hex. +XCOFF SPECIFIC OPTIONS +---------------------- + +.. option:: --no-rsrc + + Exclude resource file symbols (__rsrc) from export symbol list. + BUGS ---- diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h --- a/llvm/include/llvm/BinaryFormat/XCOFF.h +++ b/llvm/include/llvm/BinaryFormat/XCOFF.h @@ -54,6 +54,34 @@ ///< future use and should be set to 0. }; +enum XCOFFInterpret : uint16_t { + OLD_XCOFF_INTERPRET = 1, + NEW_XCOFF_INTERPRET = 2 +}; + +enum FileFlag : uint16_t { + F_RELFLG = 0x0001, ///< relocation info stripped from file + F_EXEC = 0x0002, ///< file is executable (i.e., it + ///< has a loader section) + F_LNNO = 0x0004, ///< line numbers stripped from file + F_LSYMS = 0x0008, ///< local symbols stripped from file + F_FDPR_PROF = 0x0010, ///< file was profiled with FDPR + F_FDPR_OPTI = 0x0020, ///< file was reordered with FDPR + F_DSA = 0x0040, ///< file uses Dynamic Segment Allocation (32-bit + ///< only) + F_DEP_1 = 0x0080, ///< Data Execution Protection bit 1 + F_VARPG = 0x0100, ///< executable requests using variable size pages + F_LPTEXT = 0x0400, ///< executable requires large pages for text + F_LPDATA = 0x0800, ///< executable requires large pages for data + F_DYNLOAD = 0x1000, ///< file is dynamically loadable and + ///< executable (equivalent to F_EXEC on AIX) + F_SHROBJ = 0x2000, ///< file is a shared object + F_LOADONLY = + 0x4000, ///< file can be loaded by the system loader, but it is + ///< ignored by the linker if it is a member of an archive. + F_DEP_2 = 0x8000 ///< Data Execution Protection bit 2 +}; + // x_smclas field of x_csect from system header: /usr/include/syms.h /// Storage Mapping Class definitions. enum StorageMappingClass : uint8_t { @@ -212,6 +240,8 @@ SYM_V_EXPORTED = 0x4000 }; +constexpr uint16_t VISIBILITY_MASK = 0x7000; + // Relocation types, defined in `/usr/include/reloc.h`. enum RelocationType : uint8_t { R_POS = 0x00, ///< Positive relocation. Provides the address of the referenced diff --git a/llvm/include/llvm/Object/XCOFFObjectFile.h b/llvm/include/llvm/Object/XCOFFObjectFile.h --- a/llvm/include/llvm/Object/XCOFFObjectFile.h +++ b/llvm/include/llvm/Object/XCOFFObjectFile.h @@ -60,10 +60,13 @@ return static_cast(this)->FlagAndTDataAlignment & AuxiHeaderFlagMask; } + uint8_t getTDataAlignment() const { return static_cast(this)->FlagAndTDataAlignment & AuxiHeaderTDataAlignmentMask; } + + uint16_t getVersion() const { return static_cast(this)->Version; } }; struct XCOFFAuxiliaryHeader32 : XCOFFAuxiliaryHeader { diff --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp --- a/llvm/lib/Object/XCOFFObjectFile.cpp +++ b/llvm/lib/Object/XCOFFObjectFile.cpp @@ -615,6 +615,16 @@ if (XCOFFSym.getSectionNumber() == XCOFF::N_UNDEF) Result |= SymbolRef::SF_Undefined; + // There is no visibility in old 32 bit XCOFF object file interpret. + if (is64Bit() || (auxiliaryHeader32() && (auxiliaryHeader32()->getVersion() == + NEW_XCOFF_INTERPRET))) { + uint16_t SymType = XCOFFSym.getSymbolType(); + if ((SymType & VISIBILITY_MASK) == SYM_V_HIDDEN) + Result |= SymbolRef::SF_Hidden; + + if ((SymType & VISIBILITY_MASK) == SYM_V_EXPORTED) + Result |= SymbolRef::SF_Exported; + } return Result; } diff --git a/llvm/test/tools/llvm-nm/Inputs/bitcode-sym32.ll b/llvm/test/tools/llvm-nm/Inputs/bitcode-sym32.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/Inputs/bitcode-sym32.ll @@ -0,0 +1,14 @@ +target triple = "powerpcle-unknown-linux-gnu" + +@C32 = dso_local global i32 5, align 4 +@undef_var32 = external dso_local global i32, align 4 + +define dso_local i32 @foo32(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* %i.addr, align 4 + %1 = load i32, i32* @undef_var32, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add +} diff --git a/llvm/test/tools/llvm-nm/Inputs/bitcode-sym64.ll b/llvm/test/tools/llvm-nm/Inputs/bitcode-sym64.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/Inputs/bitcode-sym64.ll @@ -0,0 +1,12 @@ +target triple = "powerpc64le-unknown-linux-gnu" + +@C64 = dso_local global i32 5, align 4 +@static_var64 = internal global i32 2, align 4 + +define dso_local signext i32 @bar64() #0 { +entry: + %0 = load i32, i32* @static_var64, align 4 + %1 = load i32, i32* @C64, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add +} diff --git a/llvm/test/tools/llvm-nm/XCOFF/export-symbols-with-invalid-section-index.test b/llvm/test/tools/llvm-nm/XCOFF/export-symbols-with-invalid-section-index.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/export-symbols-with-invalid-section-index.test @@ -0,0 +1,49 @@ +## Test the behavior of the symbol reference section. + +# RUN: yaml2obj -DFLAG=0x0002 %s -o %t.o +# RUN: llvm-nm --export-symbols %t.o 2>&1 | FileCheck %s -DFILE=%t.o + +# CHECK: llvm-nm{{(\.exe)?}}: warning: [[FILE]]: for symbol with index 4: the section index (26) is invalid +# CHECK-NEXT: export_protected_var export +# CHECK-NEXT: export_protected_var protected + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + Flags: [[FLAG]] +AuxiliaryHeader: + Magic: 0x10B + Version: 0x2 + TextSectionSize: 0x280 + DataSectionSize: 0x90 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .data + Flags: [ STYP_DATA ] +Symbols: + - Name: export_protected_var + Section: .data + Type: 0x4000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: export_protected_var + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: var_invalid + SectionIndex: 26 + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 diff --git a/llvm/test/tools/llvm-nm/XCOFF/export-symbols.test b/llvm/test/tools/llvm-nm/XCOFF/export-symbols.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/export-symbols.test @@ -0,0 +1,234 @@ +## Test the "--export-symbols" option. +## The option merges all the output of input files, sorts and prints out unique symbols from the input files. + +# RUN: yaml2obj --docnum=1 -DFLAG=0x0002 %s -o %t1.o +# RUN: yaml2obj --docnum=2 -DFLAG=0x0002 %s -o %t2.o + +## Test the following cases: +## Do not export global symbols beginning with "__sinit" , "__sterm" , "." , "(". or regular expression "^__[0-9]+__". +## Do not export hidden and internal symbols. +## Remove name prefixes of global symbols beginning with "__tf1" and "__tf9". +# RUN: llvm-nm --export-symbols %t1.o | FileCheck %s --check-prefixes=COMMON,WEAK --implicit-check-not={{.}} + +## Show that weak symbols are not exported when using the "--no-weak" option. +# RUN: llvm-nm --export-symbols --no-weak %t1.o | FileCheck --check-prefixes=COMMON %s --implicit-check-not={{.}} + +## Show that only unique symbols (with a different name or visibility) are exported. +## RUN: llvm-nm --export-symbols %t1.o %t2.o | FileCheck --check-prefixes=COMMON,WEAK,OBJ2 %s --implicit-check-not={{.}} + +# COMMON: __rsrc +# COMMON-NEXT: export_protected_var export +# COMMON-NEXT: export_protected_var protected +# OBJ2-NEXT: export_var_in_sec_obj export +# COMMON-NEXT: protected_var protected +# OBJ2-NEXT: protected_var_in_sec_obj protected +# COMMON-NEXT: tf1value +# COMMON-NEXT: tf9value +# OBJ2-NEXT: var1_in_sec_obj +# WEAK-NEXT: weak_func + +## Show that __rsrc symbols are not exported when using the "--no-rsrc" option. +# RUN: llvm-nm --export-symbols --no-rsrc %t1.o | FileCheck --check-prefixes=NO-RSRC %s --implicit-check-not={{.}} + +# NO-RSRC: export_protected_var export +# NO-RSRC-NEXT: export_protected_var protected +# NO-RSRC-NEXT: protected_var protected +# NO-RSRC-NEXT: tf1value +# NO-RSRC-NEXT: tf9value +# NO-RSRC-NEXT: weak_func + +## Show that symbols in shared object files are not exported. +## Generate XCOFF shared object file. +# RUN: yaml2obj -DFLAG=0x2000 --docnum=2 %s -o %t_shared.o +# RUN: llvm-nm --export-symbols %t_shared.o | count 0 + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + Flags: [[FLAG]] +AuxiliaryHeader: + Magic: 0x10B + Version: 0x2 + TextSectionSize: 0x280 + DataSectionSize: 0x90 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .data + Flags: [ STYP_DATA ] +Symbols: + - Name: export_protected_var + Section: .data + Type: 0x4000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: export_protected_var + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: __rsrc + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: __sinit + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x9 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: __sterm + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: .func + Section: .text + Type: 0x20 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + - Name: (func) + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0xC + - Name: __023__ + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: __tf1_tf1value + Section: .data + Type: 0x00 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: __tf9_12345678tf9value + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: weak_func + Section: .data + Type: 0x0 + StorageClass: C_WEAKEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: protected_var + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_RW + - Name: hidden_var + Section: .data + Type: 0x2000 + StorageClass: C_HIDEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: internal_var + Section: .data + Type: 0x1000 + StorageClass: C_HIDEXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + Flags: [[FLAG]] +AuxiliaryHeader: + Magic: 0x10B + Version: 0x2 + TextSectionSize: 0x280 + DataSectionSize: 0x90 +Sections: + - Name: .text + Flags: [ STYP_TEXT ] + - Name: .data + Flags: [ STYP_DATA ] +Symbols: + - Name: export_protected_var + Section: .data + Type: 0x4000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: export_protected_var + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: var1_in_sec_obj + Section: .data + Type: 0x0 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: protected_var_in_sec_obj + Section: .data + Type: 0x3000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW + - Name: export_var_in_sec_obj + Section: .data + Type: 0x4000 + StorageClass: C_EXT + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x09 + StorageMappingClass: XMC_RW diff --git a/llvm/test/tools/llvm-nm/bitcode-export-sym.test b/llvm/test/tools/llvm-nm/bitcode-export-sym.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/bitcode-export-sym.test @@ -0,0 +1,11 @@ +## Test the "--export-symbols" option with bitcode input files. + +# RUN: llvm-as -o %t32.bc %p/Inputs/bitcode-sym32.ll +# RUN: llvm-as -o %t64.bc %p/Inputs/bitcode-sym64.ll + +# RUN: llvm-nm --export-symbols %t32.bc %t64.bc | FileCheck %s --check-prefixes=CHECK --implicit-check-not={{.}} + +# CHECK: C32 +# CHECK-NEXT: C64 +# CHECK-NEXT: bar64 +# CHECK-NEXT: foo32 diff --git a/llvm/tools/llvm-nm/Opts.td b/llvm/tools/llvm-nm/Opts.td --- a/llvm/tools/llvm-nm/Opts.td +++ b/llvm/tools/llvm-nm/Opts.td @@ -17,6 +17,7 @@ def defined_only : FF<"defined-only", "Show only defined symbols">; defm demangle : BB<"demangle", "Demangle C++ symbol names", "Don't demangle symbol names">; def dynamic : FF<"dynamic", "Display dynamic symbols instead of normal symbols">; +def export_symbols : FF<"export-symbols", "Export symbol list for all inputs">; def extern_only : FF<"extern-only", "Show only external symbols">; defm format : Eq<"format", "Specify output format: bsd (default), posix, sysv, darwin, just-symbols">, MetaVarName<"">; def help : FF<"help", "Display this help">; @@ -48,6 +49,11 @@ def s : F<"s", "Dump only symbols from this segment and section name">, Group; def x : F<"x", "Print symbol entry in hex">, Group; +// XCOFF specific options. +def grp_xcoff_o : OptionGroup<"kind">, HelpText<"llvm-nm XCOFF Specific Options">; + +def no_rsrc : FF<"no-rsrc", "Exclude resource file symbols (__rsrc) from the export symbol list.">, Group; + def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>; def : FF<"portability", "Alias for --format=posix">, Alias, AliasArgs<["posix"]>; diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -17,6 +17,7 @@ #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" @@ -90,6 +91,7 @@ static bool DefinedOnly; static bool Demangle; static bool DynamicSyms; +static bool ExportSymbols; static bool ExternalOnly; static OutputFormatTy OutputFormat; static bool NoLLVMBitcode; @@ -105,6 +107,9 @@ static bool UndefinedOnly; static bool WithoutAliases; +// XCOFF-specific options. +static bool NoRsrc; + namespace { enum Radix { d, o, x }; } // namespace @@ -128,7 +133,8 @@ static StringRef ToolName; -static void warn(Error Err, Twine FileName, Twine Context = Twine()) { +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 @@ -137,8 +143,9 @@ handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { WithColor::warning(errs(), ToolName) - << FileName << ": " << (Context.str().empty() ? "" : Context + ": ") - << EI.message() << "\n"; + << (Archive.str().empty() ? FileName : Archive + "(" + FileName + ")") + << ": " << (Context.str().empty() ? "" : Context + ": ") << EI.message() + << "\n"; }); } @@ -211,6 +218,8 @@ 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 @@ -222,39 +231,67 @@ uint8_t NSect; uint16_t NDesc; std::string IndirectName; -}; -} // anonymous namespace -static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) { - bool ADefined; - // Symbol flags have been checked in the caller. - if (A.Sym.getRawDataRefImpl().p) { - uint32_t AFlags = cantFail(A.Sym.getFlags()); - ADefined = !(AFlags & SymbolRef::SF_Undefined); - } else { - ADefined = A.TypeChar != 'U'; + bool isSymbolDefined() const { + if (Sym.getRawDataRefImpl().p) { + uint32_t Flags = cantFail(Sym.getFlags()); + return !(Flags & SymbolRef::SF_Undefined); + } else + return TypeChar != 'U'; } - bool BDefined; - // Symbol flags have been checked in the caller. - if (B.Sym.getRawDataRefImpl().p) { - uint32_t BFlags = cantFail(B.Sym.getFlags()); - BDefined = !(BFlags & SymbolRef::SF_Undefined); - } else { - BDefined = B.TypeChar != 'U'; + + bool setSymFlag(const SymbolicFile &Obj) { + Expected SymFlagsOrErr = Sym.getFlags(); + if (!SymFlagsOrErr) { + // TODO: Test this error. + error(SymFlagsOrErr.takeError(), Obj.getFileName()); + return false; + } + SymFlags = *SymFlagsOrErr; + return true; } - return std::make_tuple(ADefined, A.Address, A.Name, A.Size) < - std::make_tuple(BDefined, B.Address, B.Name, B.Size); -} -static bool compareSymbolSize(const NMSymbol &A, const NMSymbol &B) { - return std::make_tuple(A.Size, A.Name, A.Address) < - std::make_tuple(B.Size, B.Name, B.Address); -} + bool shouldSkipPrintingSymbol() 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 true; + return false; + } -static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) { - return std::make_tuple(A.Name, A.Size, A.Address) < - std::make_tuple(B.Name, B.Size, B.Address); -} + bool operator<(const NMSymbol &B) const { + if (NumericSort) + return std::make_tuple(isSymbolDefined(), Address, Name, Size) < + std::make_tuple(B.isSymbolDefined(), B.Address, B.Name, B.Size); + if (SizeSort) + return std::make_tuple(Size, Name, Address) < + std::make_tuple(B.Size, B.Name, B.Address); + if (ExportSymbols) + return std::make_tuple(Name, Visibility) < + std::make_tuple(B.Name, B.Visibility); + return std::make_tuple(Name, Size, Address) < + std::make_tuple(B.Name, B.Size, B.Address); + } + + bool operator==(const NMSymbol &B) const { + if (NumericSort) + return std::make_tuple(isSymbolDefined(), Address, Name, Size) == + std::make_tuple(B.isSymbolDefined(), B.Address, B.Name, B.Size); + if (SizeSort) + return std::make_tuple(Size, Name, Address) == + std::make_tuple(B.Size, B.Name, B.Address); + if (ExportSymbols) + return std::make_tuple(Name, Visibility) == + std::make_tuple(B.Name, B.Visibility); + return std::make_tuple(Name, Size, Address) == + std::make_tuple(B.Name, B.Size, B.Address); + } +}; +} // anonymous namespace static char isSymbolList64Bit(SymbolicFile &Obj) { if (auto *IRObj = dyn_cast(&Obj)) @@ -263,7 +300,6 @@ return false; if (XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj)) return XCOFFObj->is64Bit(); - if (isa(Obj)) return false; if (TapiFile *Tapi = dyn_cast(&Obj)) @@ -658,27 +694,31 @@ } } -static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, - StringRef ArchiveName, - StringRef ArchitectureName) { - if (!NoSort) { - using Comparator = bool (*)(const NMSymbol &, const NMSymbol &); - Comparator Cmp; - if (NumericSort) - Cmp = &compareSymbolAddress; - else if (SizeSort) - Cmp = &compareSymbolSize; - else - Cmp = &compareSymbolName; +static void sortSymbolList() { + if (NoSort) + return; - if (ReverseSort) - llvm::sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) -> bool { - return Cmp(B, A); - }); - else - llvm::sort(SymbolList, Cmp); + if (ReverseSort) + llvm::stable_sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) { + return B < A; + }); + else + llvm::stable_sort(SymbolList); +} + +static void printSymbolList() { + for (const NMSymbol &Sym : SymbolList) { + if (Sym.shouldSkipPrintingSymbol()) + continue; + outs() << Sym.Name; + if (!Sym.Visibility.empty()) + outs() << ' ' << Sym.Visibility; + outs() << '\n'; } +} +static void printSymbolList(SymbolicFile &Obj, bool printName, + StringRef ArchiveName, StringRef ArchitectureName) { if (!PrintFileName) { if ((OutputFormat == bsd || OutputFormat == posix || OutputFormat == just_symbols) && @@ -725,7 +765,9 @@ } for (const NMSymbol &S : SymbolList) { - uint32_t SymFlags; + if (S.shouldSkipPrintingSymbol()) + continue; + std::string Name = S.Name; MachOObjectFile *MachO = dyn_cast(&Obj); if (Demangle) { @@ -737,25 +779,7 @@ if (Optional Opt = Fn(S.Name)) Name = *Opt; } - if (S.Sym.getRawDataRefImpl().p) { - Expected SymFlagsOrErr = S.Sym.getFlags(); - if (!SymFlagsOrErr) { - // TODO: Test this error. - error(SymFlagsOrErr.takeError(), Obj.getFileName()); - return; - } - SymFlags = *SymFlagsOrErr; - } else - SymFlags = S.SymFlags; - 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))) - continue; if (PrintFileName) writeFileName(outs(), ArchiveName, ArchitectureName); if ((OutputFormat == just_symbols || @@ -1642,6 +1666,95 @@ } } +static void exportSymbolsForXCOFF(XCOFFObjectFile *XCOFFObj, + StringRef ArchiveName) { + // Skip Shared object file. + if (XCOFFObj->getFlags() & XCOFF::F_SHROBJ) + return; + + for (const SymbolRef &Sym : XCOFFObj->symbols()) { + Expected NameOrErr = Sym.getName(); + if (!NameOrErr) { + warn(NameOrErr.takeError(), XCOFFObj->getFileName(), + "for symbol with index " + + Twine(XCOFFObj->getSymbolIndex(Sym.getRawDataRefImpl().p)), + ArchiveName); + return; + } + + // 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 = *NameOrErr; + 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("__")) { + bool HasNonNum = false; + for (unsigned int i = 2; i < SymName.size() - 2; i++) + if (SymName[i] < '0' || SymName[i] > '9') { + HasNonNum = true; + break; + } + if (!HasNonNum) + continue; + } + + if (SymName.startswith("__tf1")) + SymName = SymName.substr(6); + else if (SymName.startswith("__tf9")) + SymName = SymName.substr(14); + + if (SymName == "__rsrc" && NoRsrc) + continue; + + 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"; + if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_EXPORTED) + S.Visibility = "export"; + } + if (S.setSymFlag(*XCOFFObj)) + SymbolList.push_back(S); + } +} + static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef ArchiveName = {}, StringRef ArchitectureName = {}) { @@ -1675,6 +1788,12 @@ return; } if (!(MachO && DyldInfoOnly)) { + if (ExportSymbols && Obj.isXCOFF()) { + XCOFFObjectFile *XCOFFObj = cast(&Obj); + exportSymbolsForXCOFF(XCOFFObj, ArchiveName); + return; + } + size_t I = -1; for (BasicSymbolRef Sym : Symbols) { ++I; @@ -1734,7 +1853,8 @@ (SymbolVersions[I].IsVerDef ? "@@" : "@") + SymbolVersions[I].Name; S.Sym = Sym; - SymbolList.push_back(S); + if (S.setSymFlag(Obj)) + SymbolList.push_back(S); } } @@ -1747,6 +1867,9 @@ if (MachO && !NoDyldInfo) dumpSymbolsFromDLInfoMachO(*MachO); + if (ExportSymbols) + return; + CurrentFilename = Obj.getFileName(); if (Symbols.empty() && SymbolList.empty() && !Quiet) { @@ -1754,7 +1877,8 @@ errs() << "no symbols\n"; } - sortAndPrintSymbolList(Obj, printName, ArchiveName, ArchitectureName); + sortSymbolList(); + printSymbolList(Obj, printName, ArchiveName, ArchitectureName); } // checkMachOAndArchFlags() checks to see if the SymbolicFile is a Mach-O file @@ -1847,7 +1971,7 @@ } if (!checkMachOAndArchFlags(O, Filename)) return; - if (!PrintFileName) { + if (!PrintFileName && !ExportSymbols) { outs() << "\n"; if (isa(O)) { outs() << Filename << "(" << O->getFileName() << ")"; @@ -2169,6 +2293,12 @@ 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") @@ -2192,6 +2322,9 @@ 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; @@ -2251,6 +2384,13 @@ llvm::for_each(InputFilenames, dumpSymbolNamesFromFile); + if (ExportSymbols) { + sortSymbolList(); + SymbolList.erase(std::unique(SymbolList.begin(), SymbolList.end()), + SymbolList.end()); + printSymbolList(); + } + if (HadError) return 1; }