[llvm-objcopy] Add --strip-unneeded-symbol(s)

Differential revision: https://reviews.llvm.org/D58027


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@353919 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/tools/llvm-objcopy/COFF/strip-unneeded.test b/test/tools/llvm-objcopy/COFF/strip-unneeded.test
index 569bc8a..ca3efe4 100644
--- a/test/tools/llvm-objcopy/COFF/strip-unneeded.test
+++ b/test/tools/llvm-objcopy/COFF/strip-unneeded.test
@@ -5,6 +5,13 @@
 RUN: llvm-objcopy --strip-unneeded %t.in.o %t.out.o
 RUN: llvm-objdump -t %t.out.o | FileCheck %s --check-prefix=SYMBOLS
 
+RUN: llvm-objcopy --strip-unneeded-symbol=external_undefined_unreferenced \
+RUN:              --strip-unneeded-symbol=local_unreferenced \
+RUN:              --strip-unneeded-symbol=local_undefined_unreferenced \
+RUN:              --strip-unneeded-symbol='@feat.00' \
+RUN:               %t.in.o %t.out2.o
+RUN: cmp %t.out.o %t.out2.o
+
 SYMBOLS: SYMBOL TABLE:
 SYMBOLS-NEXT: external
 SYMBOLS-NEXT: external_undefined
diff --git a/test/tools/llvm-objcopy/ELF/strip-unneeded.test b/test/tools/llvm-objcopy/ELF/strip-unneeded.test
index 6d5682f..60d8388 100644
--- a/test/tools/llvm-objcopy/ELF/strip-unneeded.test
+++ b/test/tools/llvm-objcopy/ELF/strip-unneeded.test
@@ -5,6 +5,24 @@
 # RUN: cmp %t %t1
 # RUN: llvm-readobj --symbols %t2 | FileCheck %s
 
+# RUN: llvm-objcopy --strip-unneeded-symbol=bar \
+# RUN:              %t %t3
+# RUN: llvm-readobj --symbols %t3 | FileCheck %s --check-prefixes=STRIP-BAR,REMAIN
+
+# RUN: llvm-objcopy --regex --strip-unneeded-symbol='.*' %t %t4
+# RUN: cmp %t2 %t4
+
+# RUN: echo " bar # bar " > %t.list.txt
+# RUN: echo "foobar" >> %t.list.txt
+# RUN: echo "foobaz" >> %t.list.txt
+# RUN: echo " # comment " >> %t.list.txt
+# RUN: llvm-objcopy --strip-unneeded-symbols %t.list.txt %t %t5
+# RUN: cmp %t2 %t5
+
+# RUN: echo " .* # * - remove all " > %t.list2.txt
+# RUN: llvm-objcopy --regex --strip-unneeded-symbols %t.list2.txt %t %t6
+# RUN: cmp %t2 %t6
+
 # Verify that llvm-strip modifies the symbol table the same way.
 # RUN: llvm-strip --strip-unneeded %t
 # RUN: cmp %t %t2
@@ -141,3 +159,7 @@
 #CHECK-NEXT:    Section: .text
 #CHECK-NEXT:  }
 #CHECK-NEXT:]
+
+#STRIP-BAR-NOT: Name: bar ({{.*}})
+#REMAIN: Name: foobar
+#REMAIN: Name: foobaz
diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 72161e1..7923ef2 100644
--- a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -151,10 +151,13 @@
     if (!Sym.Referenced) {
       // With --strip-unneeded, GNU objcopy removes all unreferenced local
       // symbols, and any unreferenced undefined external.
-      if (Config.StripUnneeded &&
-          (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
-           Sym.Sym.SectionNumber == 0))
-        return true;
+      // With --strip-unneeded-symbol we strip only specific unreferenced
+      // local symbol instead of removing all of such.
+      if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
+          Sym.Sym.SectionNumber == 0)
+        if (Config.StripUnneeded ||
+            is_contained(Config.UnneededSymbolsToRemove, Sym.Name))
+          return true;
 
       // GNU objcopy keeps referenced local symbols and external symbols
       // if --discard-all is set, similar to what --strip-unneeded does,
diff --git a/tools/llvm-objcopy/CopyConfig.cpp b/tools/llvm-objcopy/CopyConfig.cpp
index f643cb4..811f8b7 100644
--- a/tools/llvm-objcopy/CopyConfig.cpp
+++ b/tools/llvm-objcopy/CopyConfig.cpp
@@ -468,6 +468,11 @@
   for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols))
     addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, Arg->getValue(),
                        UseRegex);
+  for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol))
+    Config.UnneededSymbolsToRemove.emplace_back(Arg->getValue(), UseRegex);
+  for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols))
+    addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc,
+                       Arg->getValue(), UseRegex);
   for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
     Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex);
 
diff --git a/tools/llvm-objcopy/CopyConfig.h b/tools/llvm-objcopy/CopyConfig.h
index b038604..f1d5cf2 100644
--- a/tools/llvm-objcopy/CopyConfig.h
+++ b/tools/llvm-objcopy/CopyConfig.h
@@ -91,6 +91,7 @@
   std::vector<NameOrRegex> SymbolsToKeep;
   std::vector<NameOrRegex> SymbolsToLocalize;
   std::vector<NameOrRegex> SymbolsToRemove;
+  std::vector<NameOrRegex> UnneededSymbolsToRemove;
   std::vector<NameOrRegex> SymbolsToWeaken;
   std::vector<NameOrRegex> ToRemove;
   std::vector<NameOrRegex> SymbolsToKeepGlobal;
diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index fc34387..4d2e78a 100644
--- a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -268,6 +268,12 @@
   };
 }
 
+static bool isUnneededSymbol(const Symbol &Sym) {
+  return !Sym.Referenced &&
+         (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
+         Sym.Type != STT_FILE && Sym.Type != STT_SECTION;
+}
+
 // This function handles the high level operations of GNU objcopy including
 // handling command line options. It's important to outline certain properties
 // we expect to hold of the command line operations. Any operation that "keeps"
@@ -336,7 +342,7 @@
     // The purpose of this loop is to mark symbols referenced by sections
     // (like GroupSection or RelocationSection). This way, we know which
     // symbols are still 'needed' and which are not.
-    if (Config.StripUnneeded) {
+    if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty()) {
       for (auto &Section : Obj.sections())
         Section.markSymbols();
     }
@@ -359,9 +365,9 @@
       if (is_contained(Config.SymbolsToRemove, Sym.Name))
         return true;
 
-      if (Config.StripUnneeded && !Sym.Referenced &&
-          (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
-          Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
+      if ((Config.StripUnneeded ||
+           is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) &&
+          isUnneededSymbol(Sym))
         return true;
 
       return false;
diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td
index 12d619e..b317219 100644
--- a/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/tools/llvm-objcopy/ObjcopyOpts.td
@@ -122,6 +122,15 @@
                       HelpText<"Remove all non-allocated sections">;
 def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
                      HelpText<"Remove all symbols not needed by relocations">;
+defm strip_unneeded_symbol 
+    : Eq<"strip-unneeded-symbol",
+         "Remove symbol <symbol> if it is not needed by relocations">,
+      MetaVarName<"symbol">;
+defm strip_unneeded_symbols
+    : Eq<"strip-unneeded-symbols",
+         "Reads a list of symbols from <filename> and removes them "
+         "if they are not needed by relocations">,
+      MetaVarName<"filename">;
 
 def extract_dwo
     : Flag<["-", "--"], "extract-dwo">,