[llvm-objcopy] Add support for removing sections

This change adds support for removing sections using the -R field (as
GNU objcopy does as well). This change should let us add many helpful
tests and is a proper stepping stone for adding more general kinds of
stripping.

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

llvm-svn: 315346
diff --git a/llvm/tools/llvm-objcopy/Object.cpp b/llvm/tools/llvm-objcopy/Object.cpp
index 3e6606f..bb79e42 100644
--- a/llvm/tools/llvm-objcopy/Object.cpp
+++ b/llvm/tools/llvm-objcopy/Object.cpp
@@ -37,6 +37,7 @@
   std::copy(std::begin(Contents), std::end(Contents), Buf);
 }
 
+void SectionBase::removeSectionReferences(const SectionBase *Sec) {}
 void SectionBase::initialize(SectionTableRef SecTable) {}
 void SectionBase::finalize() {}
 
@@ -138,6 +139,19 @@
   Size += this->EntrySize;
 }
 
+void SymbolTableSection::removeSectionReferences(const SectionBase *Sec) {
+  if (SymbolNames == Sec) {
+    error("String table " + SymbolNames->Name +
+          " cannot be removed because it is referenced by the symbol table " +
+          this->Name);
+  }
+  auto Iter =
+      std::remove_if(std::begin(Symbols), std::end(Symbols),
+                     [=](const SymPtr &Sym) { return Sym->DefinedIn == Sec; });
+  Size -= (std::end(Symbols) - Iter) * this->EntrySize;
+  Symbols.erase(Iter, std::end(Symbols));
+}
+
 void SymbolTableSection::initialize(SectionTableRef SecTable) {
   Size = 0;
   setStrTab(SecTable.getSectionOfType<StringTableSection>(
@@ -195,7 +209,19 @@
 }
 
 template <class SymTabType>
-void RelocationSectionBase<SymTabType>::initialize(SectionTableRef SecTable) {
+void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences(
+    const SectionBase *Sec) {
+  if (Symbols == Sec) {
+    error("Symbol table " + Symbols->Name + " cannot be removed because it is "
+                                            "referenced by the relocation "
+                                            "section " +
+          this->Name);
+  }
+}
+
+template <class SymTabType>
+void RelocSectionWithSymtabBase<SymTabType>::initialize(
+    SectionTableRef SecTable) {
   setSymTab(SecTable.getSectionOfType<SymTabType>(
       Link,
       "Link field value " + Twine(Link) + " in section " + Name + " is invalid",
@@ -210,7 +236,8 @@
     setSection(nullptr);
 }
 
-template <class SymTabType> void RelocationSectionBase<SymTabType>::finalize() {
+template <class SymTabType>
+void RelocSectionWithSymtabBase<SymTabType>::finalize() {
   this->Link = Symbols->Index;
   if (SecToApplyRel != nullptr)
     this->Info = SecToApplyRel->Index;
@@ -249,6 +276,14 @@
             Out.getBufferStart() + Offset);
 }
 
+void SectionWithStrTab::removeSectionReferences(const SectionBase *Sec) {
+  if (StrTab == Sec) {
+    error("String table " + StrTab->Name + " cannot be removed because it is "
+                                           "referenced by the section " +
+          this->Name);
+  }
+}
+
 bool SectionWithStrTab::classof(const SectionBase *S) {
   return isa<DynamicSymbolTableSection>(S) || isa<DynamicSection>(S);
 }
@@ -589,6 +624,41 @@
     Section->writeSection(Out);
 }
 
+template <class ELFT>
+void Object<ELFT>::removeSections(
+    std::function<bool(const SectionBase &)> ToRemove) {
+
+  auto Iter = std::stable_partition(
+      std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) {
+        if (ToRemove(*Sec))
+          return false;
+        if (auto RelSec = dyn_cast<RelocationSectionBase>(Sec.get()))
+          return !ToRemove(*RelSec->getSection());
+        return true;
+      });
+  if (SymbolTable != nullptr && ToRemove(*SymbolTable))
+    SymbolTable = nullptr;
+  if (ToRemove(*SectionNames)) {
+    // Right now llvm-objcopy always outputs section headers. This will not
+    // always be the case. Eventully the section header table will become
+    // optional and if no section header is output then there dosn't need to be
+    // a section header string table.
+    error("Cannot remove " + SectionNames->Name +
+          " because it is the section header string table.");
+  }
+  // Now make sure there are no remaining references to the sections that will
+  // be removed. Sometimes it is impossible to remove a reference so we emit
+  // an error here instead.
+  for (auto &RemoveSec : make_range(Iter, std::end(Sections))) {
+    for (auto &Segment : Segments)
+      Segment->removeSection(RemoveSec.get());
+    for (auto &KeepSec : make_range(std::begin(Sections), Iter))
+      KeepSec->removeSectionReferences(RemoveSec.get());
+  }
+  // Now finally get rid of them all togethor.
+  Sections.erase(Iter, std::end(Sections));
+}
+
 template <class ELFT> void ELFObject<ELFT>::sortSections() {
   // Put all sections in offset order. Maintain the ordering as closely as
   // possible while meeting that demand however.
@@ -692,7 +762,8 @@
     this->SectionNames->addString(Section->Name);
   }
   // Make sure we add the names of all the symbols.
-  this->SymbolTable->addSymbolNames();
+  if (this->SymbolTable != nullptr)
+    this->SymbolTable->addSymbolNames();
 
   sortSections();
   assignOffsets();