[llvm] Make llvm-nm's --export-symbols option work for ELF (PR #84379)

via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 7 13:24:13 PST 2024


https://github.com/bd1976bris created https://github.com/llvm/llvm-project/pull/84379

The --export-symbols option..

–help:
  –export-symbols Export symbol list for all inputs\

doc:–export-symbols
  Print sorted symbols with their visibility (if applicable), with duplicates removed.

.. was introduced in to llvm-nm for an AIX specific usecase. For non-XCOFF target it simply
 lists all symbols which seems incorrect given the option name and intended functionality.

 This PR is for implementing the option for ELF targets so that it lists the set of unique
 exports. This is the set of non-local definitions that have `default` or `protected` ELF
 visibility.

>From e0cf1f6161e5e4240ba0e4163550a3cbe101551e Mon Sep 17 00:00:00 2001
From: Ben Dunbobbin <Ben.Dunbobbin at sony.com>
Date: Thu, 7 Mar 2024 20:50:06 +0000
Subject: [PATCH] Make llvm-nm's --export-symbols option work for ELF
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The --export-symbols option..

–help:
  –export-symbols Export symbol list for all inputs\

doc:–export-symbols
  Print sorted symbols with their visibility (if applicable), with duplicates removed.

.. was introduced in to llvm-nm for an AIX specific usecase. For non-XCOFF target it simply
 lists all symbols which seems incorrect given the option name and intended functionality.

 This PR is for implementing the option for ELF targets so that it lists the set of unique
 exports. This is the set of non-local definitions that have `default` or `protected` ELF
 visibility.
---
 llvm/lib/Object/ModuleSymbolTable.cpp       |  17 ++++
 llvm/test/tools/llvm-nm/export-symbols.test | 104 ++++++++++++++++++++
 llvm/tools/llvm-nm/llvm-nm.cpp              |  13 ++-
 3 files changed, 129 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/tools/llvm-nm/export-symbols.test

diff --git a/llvm/lib/Object/ModuleSymbolTable.cpp b/llvm/lib/Object/ModuleSymbolTable.cpp
index 07f76688fa43e7..4cdb2f39ead592 100644
--- a/llvm/lib/Object/ModuleSymbolTable.cpp
+++ b/llvm/lib/Object/ModuleSymbolTable.cpp
@@ -199,6 +199,21 @@ void ModuleSymbolTable::printSymbolName(raw_ostream &OS, Symbol S) const {
   Mang.getNameWithPrefix(OS, GV, false);
 }
 
+ static bool isExportedToOtherDSO(const Triple TT, GlobalValue &GV) {
+  // TODO: Add support for other formats
+   if (!TT.isOSBinFormatELF())
+     // A defintition is exported if its non-local, and its
+     // visibility is either DEFAULT or PROTECTED. All other symbols are not
+     // exported.
+     return !GV.isDeclarationForLinker() && !GV.hasLocalLinkage() &&
+            (GV.hasDefaultVisibility() || GV.hasProtectedVisibility());
+   else if (!TT.isOSBinFormatXCOFF())
+     return true;
+
+  // TODO: Add support for other file formats
+  return false;
+ }
+
 uint32_t ModuleSymbolTable::getSymbolFlags(Symbol S) const {
   if (isa<AsmSymbol *>(S))
     return cast<AsmSymbol *>(S)->second;
@@ -214,6 +229,8 @@ uint32_t ModuleSymbolTable::getSymbolFlags(Symbol S) const {
     if (GVar->isConstant())
       Res |= BasicSymbolRef::SF_Const;
   }
+  if (isExportedToOtherDSO(Triple(FirstMod->getTargetTriple()), *GV))
+    Res |= BasicSymbolRef::SF_Exported;
   if (const GlobalObject *GO = GV->getAliaseeObject())
     if (isa<Function>(GO) || isa<GlobalIFunc>(GO))
       Res |= BasicSymbolRef::SF_Executable;
diff --git a/llvm/test/tools/llvm-nm/export-symbols.test b/llvm/test/tools/llvm-nm/export-symbols.test
new file mode 100644
index 00000000000000..99a5c252cca0de
--- /dev/null
+++ b/llvm/test/tools/llvm-nm/export-symbols.test
@@ -0,0 +1,104 @@
+## Test the "--export-symbols" option.
+## The option prints out a sorted list of the unique exports from the input files.
+
+# RUN: rm -rf %t
+# RUN: split-file %s %t
+
+# RUN: llvm-as %t/1.ll -o %t/1.bc
+# RUN: llvm-as %t/2.ll -o %t/2.bc
+# RUN: llc -filetype=obj -mtriple=x86_64-pc-linux %t/1.ll -o %t/1.o
+# RUN: llc -filetype=obj -mtriple=x86_64-pc-linux %t/2.ll -o %t/2.o
+
+## Test the following cases:
+## - Show that non-exports are ignored.
+## - Show that only unique exports (with a different name or visibility) are printed.
+## - Show that AIX special cases are not handled specially.
+# RUN: llvm-nm --export-symbols %t/1.bc %t/2.bc | FileCheck --check-prefixes=COMMON,WEAK %s --implicit-check-not={{.}}
+# RUN: llvm-nm --export-symbols %t/1.o %t/2.o | FileCheck --check-prefixes=COMMON,WEAK %s --implicit-check-not={{.}}
+# RUN: llvm-nm --export-symbols --no-weak --no-rsrc  %t/1.bc %t/2.bc %t/1.o %t/2.o | FileCheck --check-prefixes=COMMON %s --implicit-check-not={{.}}
+
+# COMMON: .
+# COMMON: __1__
+# COMMON: __rsrc
+# COMMON: __sinit
+# COMMON: __sterm
+# COMMON: __tf1
+# COMMON: __tf9
+# COMMON: c
+# COMMON: d
+# COMMON: h
+# WEAK:   w
+
+#--- 1.ll
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define void @d() {
+  ret void
+}
+
+define hidden void @h() {
+  ret void
+}
+
+declare void @u()
+
+declare i32 @r(...)
+
+declare extern_weak void @wr()
+
+define internal void @l() {
+  ret void
+}
+
+ at c = common global i32 0
+
+define weak void @w() {
+  ret void
+}
+
+;; special cases from the AIX implementation.
+
+define void @__sinit() {
+  ret void
+}
+
+define void @__sterm() {
+  ret void
+}
+
+define void @.() {
+  ret void
+}
+
+;define void @(() {
+;  ret void
+;}
+
+define void @__1__() {
+  ret void
+}
+
+define void @__tf1() {
+  ret void
+}
+
+define void @__tf9() {
+  ret void
+}
+
+define void @__rsrc() {
+  ret void
+}
+
+#--- 2.ll
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+define protected void @d() {
+  ret void
+}
+
+define void @h() {
+  ret void
+}
diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp
index e3b81451fcac91..75d01317a2f026 100644
--- a/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -254,16 +254,17 @@ struct NMSymbol {
     return true;
   }
 
-  bool shouldPrint() const {
+  bool shouldPrint(bool OnlyExported) const {
     bool Undefined = SymFlags & SymbolRef::SF_Undefined;
     bool Global = SymFlags & SymbolRef::SF_Global;
     bool Weak = SymFlags & SymbolRef::SF_Weak;
+    bool Export = SymFlags & SymbolRef::SF_Exported;
     bool FormatSpecific = SymFlags & SymbolRef::SF_FormatSpecific;
     if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
         (!Global && ExternalOnly) || (Weak && NoWeakSymbols) ||
         (FormatSpecific && !(SpecialSyms || DebugSyms)))
       return false;
-    return true;
+    return OnlyExported ? Export : true;
   }
 };
 
@@ -777,7 +778,7 @@ static void printSymbolList(SymbolicFile &Obj,
   }
 
   for (const NMSymbol &S : SymbolList) {
-    if (!S.shouldPrint())
+    if (!S.shouldPrint(false /*OnlyExported*/))
       continue;
 
     std::string Name = S.Name;
@@ -1768,8 +1769,10 @@ static void getXCOFFExports(XCOFFObjectFile *XCOFFObj,
       else if ((SymType & XCOFF::VISIBILITY_MASK) == XCOFF::SYM_V_EXPORTED)
         S.Visibility = "export";
     }
-    if (S.initializeFlags(*XCOFFObj))
+    if (S.initializeFlags(*XCOFFObj)) {
+      S.SymFlags |= SymbolRef::SF_Exported;
       SymbolList.push_back(S);
+    }
   }
 }
 
@@ -2393,7 +2396,7 @@ exportSymbolNamesFromFiles(const std::vector<std::string> &InputFilenames) {
 
   // Delete symbols which should not be printed from SymolList.
   llvm::erase_if(SymbolList,
-                 [](const NMSymbol &s) { return !s.shouldPrint(); });
+                 [](const NMSymbol &s) { return !s.shouldPrint(true /*OnlyExported*/); });
   sortSymbolList(SymbolList);
   SymbolList.erase(std::unique(SymbolList.begin(), SymbolList.end()),
                    SymbolList.end());



More information about the llvm-commits mailing list