[llvm-objcopy] Add support for --only-keep/-j and --keep

This change adds support for the --only-keep option and the -j alias as well.
A common use case for these being used together is to dump a specific section's
data. Additionally the --keep option is added (GNU objcopy doesn't have this)
to avoid removing a bunch of things. This allows people to err on the side of
stripping aggressively and then to keep the specific bits that they need for
their application.

Differential Revision: https://reviews.llvm.org/D39021

llvm-svn: 319467
diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 09553e8..615667b 100644
--- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -89,6 +89,13 @@
 static cl::opt<bool>
     StripAllGNU("strip-all-gnu",
                 cl::desc("Removes symbol, relocation, and debug information"));
+static cl::list<std::string> Keep("keep", cl::desc("Keep <section>"),
+                                  cl::value_desc("section"));
+static cl::list<std::string> OnlyKeep("only-keep",
+                                      cl::desc("Remove all but <section>"),
+                                      cl::value_desc("section"));
+static cl::alias OnlyKeepA("j", cl::desc("Alias for only-keep"),
+                           cl::aliasopt(OnlyKeep));
 static cl::opt<bool> StripDebug("strip-debug",
                                 cl::desc("Removes all debug information"));
 static cl::opt<bool> StripSections("strip-sections",
@@ -150,6 +157,13 @@
   WriteObjectFile(DWOFile, File);
 }
 
+// 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"
+// should keep regardless of a remove. Additionally any removal should respect
+// any previous removals. Lastly whether or not something is removed shouldn't
+// depend a) on the order the options occur in or b) on some opaque priority
+// system. The only priority is that keeps/copies overrule removes.
 template <class ELFT>
 void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
   std::unique_ptr<Object<ELFT>> Obj;
@@ -166,6 +180,8 @@
 
   SectionPred RemovePred = [](const SectionBase &) { return false; };
 
+  // Removes:
+
   if (!ToRemove.empty()) {
     RemovePred = [&](const SectionBase &Sec) {
       return std::find(std::begin(ToRemove), std::end(ToRemove), Sec.Name) !=
@@ -234,6 +250,43 @@
       return (Sec.Flags & SHF_ALLOC) == 0;
     };
 
+  // Explicit copies:
+
+  if (!OnlyKeep.empty()) {
+    RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+      // Explicitly keep these sections regardless of previous removes.
+      if (std::find(std::begin(OnlyKeep), std::end(OnlyKeep), Sec.Name) !=
+          std::end(OnlyKeep))
+        return false;
+
+      // Allow all implicit removes.
+      if (RemovePred(Sec)) {
+        return true;
+      }
+
+      // Keep special sections.
+      if (Obj->getSectionHeaderStrTab() == &Sec) {
+        return false;
+      }
+      if (Obj->getSymTab() == &Sec || Obj->getSymTab()->getStrTab() == &Sec) {
+        return false;
+      }
+      // Remove everything else.
+      return true;
+    };
+  }
+
+  if (!Keep.empty()) {
+    RemovePred = [RemovePred](const SectionBase &Sec) {
+      // Explicitly keep these sections regardless of previous removes.
+      if (std::find(std::begin(Keep), std::end(Keep), Sec.Name) !=
+          std::end(Keep))
+        return false;
+      // Otherwise defer to RemovePred.
+      return RemovePred(Sec);
+    };
+  }
+
   Obj->removeSections(RemovePred);
   Obj->finalize();
   WriteObjectFile(*Obj, OutputFilename.getValue());