[llvm] fd3ba1f - Title: Export unique symbol list with llvm-nm new option "--export-symbols"

via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 17 08:37:56 PST 2022


Author: zhijian
Date: 2022-02-17T11:37:33-05:00
New Revision: fd3ba1f862f54811ff9f4663ff298ff02d9c3b70

URL: https://github.com/llvm/llvm-project/commit/fd3ba1f862f54811ff9f4663ff298ff02d9c3b70
DIFF: https://github.com/llvm/llvm-project/commit/fd3ba1f862f54811ff9f4663ff298ff02d9c3b70.diff

LOG: Title: Export unique symbol list with llvm-nm new option "--export-symbols"

Summary:

the patch implement of following functionality.
1. export the symbols from archive or object files.
2. sort the export symbols. (based on same symbol name and visibility)
3. delete the duplicate export symbols (based on same symbol name and visibility)
4. print out the  unique and sorted export symbols (print the symbol name and visibility).

there are two new options are add in the patch
1. --export-symbols (enable the functionality of export unique symbol)
2. --no-rsrc (exclude the symbol name begin with "__rsrc" from be exporting from xcoff object file)

Export symbol list for xcoff object file has the same functionality as
The patch has the same functionality as
https://www.ibm.com/docs/en/xl-c-aix/13.1.0?topic=library-exporting-symbols-createexportlist-utility

Reviewers: James Henderson,Fangrui Song
Differential Revision: https://reviews.llvm.org/D112735

Added: 
    llvm/test/tools/llvm-nm/XCOFF/export-symbols.test
    llvm/test/tools/llvm-nm/bitcode-export-sym.test

Modified: 
    llvm/docs/CommandGuide/llvm-nm.rst
    llvm/include/llvm/BinaryFormat/XCOFF.h
    llvm/include/llvm/Object/XCOFFObjectFile.h
    llvm/lib/Object/XCOFFObjectFile.cpp
    llvm/tools/llvm-nm/Opts.td
    llvm/tools/llvm-nm/llvm-nm.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-nm.rst b/llvm/docs/CommandGuide/llvm-nm.rst
index 4b8db71e6a14..8fc2b214cf05 100644
--- a/llvm/docs/CommandGuide/llvm-nm.rst
+++ b/llvm/docs/CommandGuide/llvm-nm.rst
@@ -157,6 +157,11 @@ OPTIONS
 
  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
@@ -282,6 +287,13 @@ MACH-O SPECIFIC OPTIONS
 
  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
index cffd8618f1e3..5d23ec5cd911 100644
--- a/llvm/include/llvm/BinaryFormat/XCOFF.h
+++ b/llvm/include/llvm/BinaryFormat/XCOFF.h
@@ -54,6 +54,34 @@ enum AuxHeaderFlags64 : uint16_t {
                         ///< 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 @@ enum VisibilityType : uint16_t {
   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
index ac911e534f34..5aad03b888fc 100644
--- a/llvm/include/llvm/Object/XCOFFObjectFile.h
+++ b/llvm/include/llvm/Object/XCOFFObjectFile.h
@@ -60,10 +60,13 @@ template <typename T> struct XCOFFAuxiliaryHeader {
     return static_cast<const T *>(this)->FlagAndTDataAlignment &
            AuxiHeaderFlagMask;
   }
+
   uint8_t getTDataAlignment() const {
     return static_cast<const T *>(this)->FlagAndTDataAlignment &
            AuxiHeaderTDataAlignmentMask;
   }
+
+  uint16_t getVersion() const { return static_cast<const T *>(this)->Version; }
 };
 
 struct XCOFFAuxiliaryHeader32 : XCOFFAuxiliaryHeader<XCOFFAuxiliaryHeader32> {

diff  --git a/llvm/lib/Object/XCOFFObjectFile.cpp b/llvm/lib/Object/XCOFFObjectFile.cpp
index f2f6d700ddd8..d9ecb0aff6bd 100644
--- a/llvm/lib/Object/XCOFFObjectFile.cpp
+++ b/llvm/lib/Object/XCOFFObjectFile.cpp
@@ -615,6 +615,16 @@ Expected<uint32_t> XCOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
   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-symbols.test b/llvm/test/tools/llvm-nm/XCOFF/export-symbols.test
new file mode 100644
index 000000000000..8cedd2e20400
--- /dev/null
+++ b/llvm/test/tools/llvm-nm/XCOFF/export-symbols.test
@@ -0,0 +1,340 @@
+## 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
+# RUN: yaml2obj --docnum=2 -DFLAG=0x0002 -DSECT=26 %s -o %t2_invalid.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,RSRC --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,RSRC  %s --implicit-check-not={{.}}
+
+## Show that only unique symbols (with a 
diff erent name or visibility) are exported.
+## RUN: llvm-nm --export-symbols %t1.o %t2.o | FileCheck --check-prefixes=COMMON,WEAK,OBJ2,RSRC %s --implicit-check-not={{.}}
+
+## 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=COMMON,WEAK %s --implicit-check-not={{.}}
+
+# COMMON:      023__
+# COMMON-NEXT: __023
+# COMMON-NEXT: __02er02__
+# COMMON-NEXT: ____
+# RSRC-NEXT:   __rsrc
+# COMMON-NEXT: __rsrc export
+# COMMON-NEXT: __tf2value
+# 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
+
+## Test the behavior of the symbol reference section.
+# RUN: llvm-nm --export-symbols --no-rsrc %t2_invalid.o 2>&1 | \
+# RUN:   FileCheck -DFILE=%t2_invalid.o --check-prefixes=INVALID %s
+
+# INVALID:      llvm-nm{{(\.exe)?}}: warning: [[FILE]]: for symbol with index 8: the section index (26) is invalid
+# INVALID-NEXT: export_protected_var export
+# INVALID-NEXT: export_protected_var protected
+# INVALID-NEXT: protected_var_in_sec_obj protected
+# INVALID-NEXT: var1_in_sec_obj
+
+## 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
+Sections:
+  - Name:            .text
+    Flags:           [ STYP_TEXT ]
+  - Name:            .data
+    Flags:           [ STYP_DATA ]
+  - Name:            .bss
+    Flags:           [ STYP_DATA ]
+  - Name:            .debug
+    Flags:           [ STYP_DEBUG ]
+Symbols:
+  - Name:            export_protected_var
+    Section:         .data
+## Exported visibility.
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+  - Name:            export_protected_var
+    Section:         .data
+## Protected visibility.
+    Type:            0x3000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+  - Name:            __rsrc
+    Section:         .data
+## No visibility.
+    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:         .text
+    Type:            0x00
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RO
+  - Name:            __tf9_12345678tf9value
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+  - Name:            __tf2value
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_HIDEXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x21
+       StorageMappingClass:    XMC_TC
+  - Name:            __tf2value
+    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:         .bss
+    Type:            0x3000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x02
+       StorageMappingClass:    XMC_RW
+  - Name:            hidden_var
+    Section:         .data
+## Hidden visibility.
+    Type:            0x2000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+  - Name:            internal_var
+    Section:         .data
+## Internal visibility.
+    Type:            0x1000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+## A symbol that is neither text, nor data, nor bss.
+  - Name:            debug
+    Section:         .debug
+## Empty symbol name.
+  - Name:            ""
+    Section:         .data
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_TC
+  - Name:            undef_var
+    SectionIndex:    0
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x20
+       StorageMappingClass:    XMC_UA
+## Do not export not global symbol.
+  - Name:            hidext_var
+    Section:         .data
+## Protected visibility.
+    Type:            0x3000
+    StorageClass:    C_HIDEXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+## Symbol should not be filtered out by option --no-rsrc.
+  - Name:            __tf1___rsrc
+    Section:         .data
+    Type:            0x4000
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+       SectionOrLength:        0x4
+## Following symbols should not be filtered out by regular expression "^__[0-9]+__".
+  - Name:            __023
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+       SectionOrLength:        0x4
+  - Name:            023__
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+       SectionOrLength:        0x4
+  - Name:            ____
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+       SectionOrLength:        0x4
+  - Name:            __02er02__
+    Section:         .data
+    Type:            0x0
+    StorageClass:    C_EXT
+    AuxEntries:
+     - Type:                   AUX_CSECT
+       SymbolAlignmentAndType: 0x09
+       StorageMappingClass:    XMC_RW
+       SectionOrLength:        0x4
+
+--- !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
+    SectionIndex:    [[SECT=2]]
+    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
index 000000000000..9866666ffa77
--- /dev/null
+++ b/llvm/test/tools/llvm-nm/bitcode-export-sym.test
@@ -0,0 +1,12 @@
+# REQUIRES: powerpc-registered-target
+## 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
index 434a70b1fbc9..6cb530db72f4 100644
--- a/llvm/tools/llvm-nm/Opts.td
+++ b/llvm/tools/llvm-nm/Opts.td
@@ -18,6 +18,7 @@ def debug_syms : FF<"debug-syms", "Show all symbols, even debugger only">;
 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<"<format>">;
 def help : FF<"help", "Display this help">;
@@ -49,6 +50,11 @@ def no_dyldinfo : FF<"no-dyldinfo", "Don't add any symbols from the dyldinfo">,
 def s : F<"s", "Dump only symbols from this segment and section name">, Group<grp_mach_o>;
 def x : F<"x", "Print symbol entry in hex">, Group<grp_mach_o>;
 
+// 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<grp_xcoff_o>;
+
 def : FF<"just-symbol-name", "Alias for --format=just-symbols">, Alias<format_EQ>, AliasArgs<["just-symbols"]>, Flags<[HelpHidden]>;
 def : FF<"portability", "Alias for --format=posix">, Alias<format_EQ>, AliasArgs<["posix"]>;
 

diff  --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index 2196faf933b8..9324cafa7f11 100644
--- 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"
@@ -92,6 +93,7 @@ static bool DebugSyms;
 static bool DefinedOnly;
 static bool Demangle;
 static bool DynamicSyms;
+static bool ExportSymbols;
 static bool ExternalOnly;
 static OutputFormatTy OutputFormat;
 static bool NoLLVMBitcode;
@@ -107,6 +109,9 @@ static bool SizeSort;
 static bool UndefinedOnly;
 static bool WithoutAliases;
 
+// XCOFF-specific options.
+static bool NoRsrc;
+
 namespace {
 enum Radix { d, o, x };
 } // namespace
@@ -130,7 +135,8 @@ static bool HadError = false;
 
 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
@@ -139,8 +145,9 @@ static void warn(Error Err, Twine FileName, Twine Context = Twine()) {
 
   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";
   });
 }
 
@@ -213,6 +220,8 @@ struct NMSymbol {
   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
@@ -232,6 +241,29 @@ struct NMSymbol {
     }
     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) {
@@ -241,11 +273,17 @@ bool operator<(const NMSymbol &A, const NMSymbol &B) {
   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 char isSymbolList64Bit(SymbolicFile &Obj) {
@@ -659,6 +697,15 @@ static void sortSymbolList() {
     llvm::sort(SymbolList);
 }
 
+static void printExportSymbolList() {
+  for (const NMSymbol &Sym : SymbolList) {
+    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) {
@@ -707,25 +754,7 @@ static void printSymbolList(SymbolicFile &Obj, bool printName,
   }
 
   for (const NMSymbol &S : SymbolList) {
-    uint32_t SymFlags;
-    if (S.Sym.getRawDataRefImpl().p) {
-      Expected<uint32_t> 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 (!S.shouldPrint())
       continue;
 
     std::string Name = S.Name;
@@ -1638,11 +1667,93 @@ static bool shouldDump(SymbolicFile &Obj) {
                                 : BitMode != BitModeTy::Bit64;
 }
 
+static void getXCOFFExports(XCOFFObjectFile *XCOFFObj, 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 void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
                                       StringRef ArchiveName = {},
                                       StringRef ArchitectureName = {}) {
   if (!shouldDump(Obj))
     return;
+
+  if (ExportSymbols && Obj.isXCOFF()) {
+    XCOFFObjectFile *XCOFFObj = cast<XCOFFObjectFile>(&Obj);
+    getXCOFFExports(XCOFFObj, ArchiveName);
+    return;
+  }
+
   auto Symbols = Obj.symbols();
   std::vector<VersionEntry> SymbolVersions;
   if (DynamicSyms) {
@@ -1672,6 +1783,7 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
     if (Nsect == 0)
       return;
   }
+
   if (!(MachO && DyldInfoOnly)) {
     size_t I = -1;
     for (BasicSymbolRef Sym : Symbols) {
@@ -1732,7 +1844,8 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
             (SymbolVersions[I].IsVerDef ? "@@" : "@") + SymbolVersions[I].Name;
 
       S.Sym = Sym;
-      SymbolList.push_back(S);
+      if (S.initializeFlags(Obj))
+        SymbolList.push_back(S);
     }
   }
 
@@ -1745,6 +1858,9 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
   if (MachO && !NoDyldInfo)
     dumpSymbolsFromDLInfoMachO(*MachO);
 
+  if (ExportSymbols)
+    return;
+
   CurrentFilename = Obj.getFileName();
 
   if (Symbols.empty() && SymbolList.empty() && !Quiet) {
@@ -1846,7 +1962,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
           }
           if (!checkMachOAndArchFlags(O, Filename))
             return;
-          if (!PrintFileName && shouldDump(*O)) {
+          if (!PrintFileName && shouldDump(*O) && !ExportSymbols) {
             outs() << "\n";
             if (isa<MachOObjectFile>(O)) {
               outs() << Filename << "(" << O->getFileName() << ")";
@@ -2168,6 +2284,12 @@ int main(int argc, char **argv) {
   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")
@@ -2203,6 +2325,9 @@ int main(int argc, char **argv) {
   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;
@@ -2262,6 +2387,18 @@ int main(int argc, char **argv) {
 
   llvm::for_each(InputFilenames, dumpSymbolNamesFromFile);
 
+  if (ExportSymbols) {
+    // Delete symbols which should not be printed from SymolList.
+    SymbolList.erase(
+        std::remove_if(SymbolList.begin(), SymbolList.end(),
+                       [](const NMSymbol &s) { return !s.shouldPrint(); }),
+        SymbolList.end());
+    sortSymbolList();
+    SymbolList.erase(std::unique(SymbolList.begin(), SymbolList.end()),
+                     SymbolList.end());
+    printExportSymbolList();
+  }
+
   if (HadError)
     return 1;
 }


        


More information about the llvm-commits mailing list