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 symbol 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 the rsrc symbol from export symbol list for XCOFF object file. + 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/XCOFF/export-sym-list-with-invalid-section-index.test b/llvm/test/tools/llvm-nm/XCOFF/export-sym-list-with-invalid-section-index.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/export-sym-list-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-sym-list.test b/llvm/test/tools/llvm-nm/XCOFF/export-sym-list.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/export-sym-list.test @@ -0,0 +1,235 @@ +## Test the option "--export-symbols" of llvm-nm. +## The option merge all the output of input files,sort and print out unique symbols from object file. + +# 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 the same name and 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 | FileCheck %s --allow-empty --implicit-check-not={{.}} + +--- !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,35 @@ +## Test the option "--export-symbols" of llvm-nm for bitcode object file. + +# RUN: echo "target triple = \"powerpcle-unknown-linux-gnu\"" > %t32.ll +# RUN: echo "@C32 = dso_local global i32 5, align 4" >> %t32.ll +# RUN: echo "@undef_var32 = external dso_local global i32, align 4" >> %t32.ll +# RUN: echo "define dso_local i32 @foo32(i32 %i) {" >> %t32.ll +# RUN: echo "entry:" >> %t32.ll +# RUN: echo " %i.addr = alloca i32, align 4" >> %t32.ll +# RUN: echo " store i32 %i, i32* %i.addr, align 4" >> %t32.ll +# RUN: echo " %0 = load i32, i32* %i.addr, align 4" >> %t32.ll +# RUN: echo " %1 = load i32, i32* @undef_var32, align 4" >> %t32.ll +# RUN: echo " %add = add nsw i32 %0, %1" >> %t32.ll +# RUN: echo " ret i32 %add" >> %t32.ll +# RUN: echo "}" >> %t32.ll + +# RUN: echo "target triple = \"powerpc64le-unknown-linux-gnu\"" > %t64.ll +# RUN: echo "@C64 = dso_local global i32 5, align 4" >> %t64.ll +# RUN: echo "@static_var64 = internal global i32 2, align 4" >> %t64.ll +# RUN: echo "define dso_local signext i32 @bar64() {" >> %t64.ll +# RUN: echo "entry:" >> %t64.ll +# RUN: echo " %0 = load i32, i32* @static_var64, align 4" >> %t64.ll +# RUN: echo " %1 = load i32, i32* @C64, align 4" >> %t64.ll +# RUN: echo " %add = add nsw i32 %0, %1" >> %t64.ll +# RUN: echo " ret i32 %add" >> %t64.ll +# RUN: echo "}" >> %t64.ll + +# RUN: llvm-as -o %t32.bc %t32.ll +# RUN: llvm-as -o %t64.bc %t64.ll + +# RUN: llvm-nm --export-symbols %t1.bc %t2.bc | FileCheck %s --check-prefixes=CHECK --implicit-check-not={{.}} + +# CHECK: C1 +# CHECK-NEXT: C2 +# CHECK-NEXT: bar +# CHECK-NEXT: foo 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 object files or archives">; 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,12 @@ 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 the rsrc symbol from export symbol list" + "for xcoff (requires --export-symbols)">, 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" @@ -104,6 +105,10 @@ static bool SizeSort; static bool UndefinedOnly; static bool WithoutAliases; +static bool ExportSymbols; + +// XCOFF-specific options. +static bool NoRsrc; namespace { enum Radix { d, o, x }; @@ -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 @@ -225,8 +234,8 @@ }; } // anonymous namespace -static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) { - bool ADefined; +static void prepareForCompSymbolAddress(const NMSymbol &A, const NMSymbol &B, + bool &ADefined, bool &BDefined) { // Symbol flags have been checked in the caller. if (A.Sym.getRawDataRefImpl().p) { uint32_t AFlags = cantFail(A.Sym.getFlags()); @@ -234,7 +243,7 @@ } else { ADefined = A.TypeChar != 'U'; } - bool BDefined; + // Symbol flags have been checked in the caller. if (B.Sym.getRawDataRefImpl().p) { uint32_t BFlags = cantFail(B.Sym.getFlags()); @@ -242,20 +251,54 @@ } else { BDefined = B.TypeChar != 'U'; } +} + +static bool lessSymbolAddress(const NMSymbol &A, const NMSymbol &B) { + bool ADefined; + bool BDefined; + prepareForCompSymbolAddress(A, B, ADefined, BDefined); 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) { +static bool equalSymbolAddress(const NMSymbol &A, const NMSymbol &B) { + bool ADefined; + bool BDefined; + prepareForCompSymbolAddress(A, B, ADefined, BDefined); + return std::make_tuple(ADefined, A.Address, A.Name, A.Size) == + std::make_tuple(BDefined, B.Address, B.Name, B.Size); +} + +static bool lessSymbolSize(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); } -static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) { +static bool equalSymbolSize(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); +} + +static bool lessSymbolName(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); } +static bool equalSymbolName(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); +} + +static bool lessSymbolNameVisibility(const NMSymbol &A, const NMSymbol &B) { + return std::make_tuple(A.Name, A.Visibility) < + std::make_tuple(B.Name, B.Visibility); +} + +static bool equalSymbolNameVisibility(const NMSymbol &A, const NMSymbol &B) { + return std::make_tuple(A.Name, A.Visibility) == + std::make_tuple(B.Name, B.Visibility); +} + static char isSymbolList64Bit(SymbolicFile &Obj) { if (auto *IRObj = dyn_cast(&Obj)) return Triple(IRObj->getTargetTriple()).isArch64Bit(); @@ -263,7 +306,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 +700,76 @@ } } -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; +using SymbolComparator = bool (*)(const NMSymbol &, const NMSymbol &); - if (ReverseSort) - llvm::sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) -> bool { - return Cmp(B, A); - }); - else - llvm::sort(SymbolList, Cmp); +static SymbolComparator getSymbolLessComparator() { + if (NumericSort) + return &lessSymbolAddress; + if (SizeSort) + return &lessSymbolSize; + if (ExportSymbols) + return &lessSymbolNameVisibility; + return &lessSymbolName; +} + +static SymbolComparator getSymbolEqualComparator() { + if (NumericSort) + return &equalSymbolAddress; + if (SizeSort) + return &equalSymbolSize; + if (ExportSymbols) + return &equalSymbolNameVisibility; + return &equalSymbolName; +} + +static void sortSymbolList() { + if (NoSort) + return; + + if (ReverseSort) + llvm::stable_sort(SymbolList, [=](const NMSymbol &A, const NMSymbol &B) { + return getSymbolLessComparator()(B, A); + }); + else + llvm::stable_sort(SymbolList, getSymbolLessComparator()); +} + +static bool setSymFlag(NMSymbol &S, const SymbolicFile &Obj) { + Expected SymFlagsOrErr = S.Sym.getFlags(); + if (!SymFlagsOrErr) { + // TODO: Test this error. + error(SymFlagsOrErr.takeError(), Obj.getFileName()); + return false; } + S.SymFlags = *SymFlagsOrErr; + return true; +} + +static bool isSymPrintSkip(const uint32_t 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))) + return true; + return false; +} + +static void printSymbolList() { + for (const auto &Sym : SymbolList) { + if (isSymPrintSkip(Sym.SymFlags)) + 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 +816,6 @@ } for (const NMSymbol &S : SymbolList) { - uint32_t SymFlags; std::string Name = S.Name; MachOObjectFile *MachO = dyn_cast(&Obj); if (Demangle) { @@ -737,25 +827,10 @@ 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))) + + if (isSymPrintSkip(S.SymFlags)) continue; + if (PrintFileName) writeFileName(outs(), ArchiveName, ArchitectureName); if ((OutputFormat == just_symbols || @@ -1642,6 +1717,96 @@ } } +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 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 (setSymFlag(S, *XCOFFObj)) + SymbolList.push_back(S); + } +} + static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, StringRef ArchiveName = {}, StringRef ArchitectureName = {}) { @@ -1675,6 +1840,14 @@ return; } if (!(MachO && DyldInfoOnly)) { + + if (ExportSymbols && Obj.isXCOFF()) { + XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj); + assert(XCOFFObj && "Not an XCOFFObjectFile."); + exportSymbolsForXCOFF(XCOFFObj, ArchiveName); + return; + } + size_t I = -1; for (BasicSymbolRef Sym : Symbols) { ++I; @@ -1734,7 +1907,8 @@ (SymbolVersions[I].IsVerDef ? "@@" : "@") + SymbolVersions[I].Name; S.Sym = Sym; - SymbolList.push_back(S); + if (setSymFlag(S, Obj)) + SymbolList.push_back(S); } } @@ -1747,6 +1921,9 @@ if (MachO && !NoDyldInfo) dumpSymbolsFromDLInfoMachO(*MachO); + if (ExportSymbols) + return; + CurrentFilename = Obj.getFileName(); if (Symbols.empty() && SymbolList.empty() && !Quiet) { @@ -1754,7 +1931,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 +2025,7 @@ } if (!checkMachOAndArchFlags(O, Filename)) return; - if (!PrintFileName) { + if (!PrintFileName && !ExportSymbols) { outs() << "\n"; if (isa(O)) { outs() << Filename << "(" << O->getFileName() << ")"; @@ -2169,6 +2347,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 +2376,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 +2438,14 @@ llvm::for_each(InputFilenames, dumpSymbolNamesFromFile); + if (ExportSymbols) { + sortSymbolList(); + SymbolList.erase(std::unique(SymbolList.begin(), SymbolList.end(), + getSymbolEqualComparator()), + SymbolList.end()); + printSymbolList(); + } + if (HadError) return 1; }