]> granicus.if.org Git - llvm/commitdiff
[llvm-objcopy] Add support for --only-keep/-j and --keep
authorJake Ehrlich <jakehehrlich@google.com>
Thu, 30 Nov 2017 20:14:53 +0000 (20:14 +0000)
committerJake Ehrlich <jakehehrlich@google.com>
Thu, 30 Nov 2017 20:14:53 +0000 (20:14 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@319467 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
test/tools/llvm-objcopy/basic-keep.test [new file with mode: 0644]
test/tools/llvm-objcopy/basic-only-keep.test [new file with mode: 0644]
test/tools/llvm-objcopy/dump-section.test [new file with mode: 0644]
test/tools/llvm-objcopy/explicit-keep-remove.test [new file with mode: 0644]
test/tools/llvm-objcopy/explicit-only-keep-remove.test [new file with mode: 0644]
test/tools/llvm-objcopy/keep-many.test [new file with mode: 0644]
test/tools/llvm-objcopy/keep-only-keep.test [new file with mode: 0644]
test/tools/llvm-objcopy/only-keep-many.test [new file with mode: 0644]
test/tools/llvm-objcopy/only-keep-remove-strtab.test [new file with mode: 0644]
test/tools/llvm-objcopy/only-keep-strip-non-alloc.test [new file with mode: 0644]
test/tools/llvm-objcopy/strip-sections-keep.test [new file with mode: 0644]
test/tools/llvm-objcopy/strip-sections-only-keep.test [new file with mode: 0644]
tools/llvm-objcopy/Object.h
tools/llvm-objcopy/llvm-objcopy.cpp

diff --git a/test/tools/llvm-objcopy/basic-keep.test b/test/tools/llvm-objcopy/basic-keep.test
new file mode 100644 (file)
index 0000000..2ea4ea3
--- /dev/null
@@ -0,0 +1,19 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -strip-non-alloc -keep=.test %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 3
+
+# CHECK:     Name: .test
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/basic-only-keep.test b/test/tools/llvm-objcopy/basic-only-keep.test
new file mode 100644 (file)
index 0000000..4d97012
--- /dev/null
@@ -0,0 +1,23 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -only-keep=.test %t %t2
+# RUN: llvm-objcopy -j=.test %t %t3
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+# RUN: diff %t2 %t3
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 5
+
+# CHECK:     Name: .test
+# CHECK:     Name: .symtab
+# CHECK:     Name: .strtab
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/dump-section.test b/test/tools/llvm-objcopy/dump-section.test
new file mode 100644 (file)
index 0000000..123f473
--- /dev/null
@@ -0,0 +1,28 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -O binary -j .text %t %t2
+# RUN: llvm-objcopy -O binary -only-keep .text %t %t3
+# RUN: od -t x1 %t2 | FileCheck %s
+# RUN: wc -c %t2 | FileCheck %s --check-prefix=SIZE
+# RUN: diff %t2 %t3
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    AddressAlign:    0x0000000000001000
+    Content:         "DEADBEEF"
+ProgramHeaders:
+- Type: PT_LOAD
+  Flags: [ PF_X, PF_R ]
+  Sections:
+    - Section: .text
+
+#CHECK: 0000000 de ad be ef
+
+#SIZE: 4
diff --git a/test/tools/llvm-objcopy/explicit-keep-remove.test b/test/tools/llvm-objcopy/explicit-keep-remove.test
new file mode 100644 (file)
index 0000000..5ebd2a5
--- /dev/null
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -R=.test -keep=.test %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 5
+
+# CHECK:     Name: .test
+# CHECK:     Name: .symtab
+# CHECK:     Name: .strtab
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/explicit-only-keep-remove.test b/test/tools/llvm-objcopy/explicit-only-keep-remove.test
new file mode 100644 (file)
index 0000000..10d49e1
--- /dev/null
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -R=.test -only-keep=.test %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 5
+
+# CHECK:     Name: .test
+# CHECK:     Name: .symtab
+# CHECK:     Name: .strtab
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/keep-many.test b/test/tools/llvm-objcopy/keep-many.test
new file mode 100644 (file)
index 0000000..6627377
--- /dev/null
@@ -0,0 +1,27 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -strip-non-alloc -keep=.test -keep=.test3 %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+  - Name:            .test2
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+  - Name:            .test3
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+
+# CHECK: SectionHeaderCount: 4
+
+# CHECK:     Name: .test
+# CHECK:     Name: .test3
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/keep-only-keep.test b/test/tools/llvm-objcopy/keep-only-keep.test
new file mode 100644 (file)
index 0000000..b1f08fe
--- /dev/null
@@ -0,0 +1,27 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -keep=.test2 -only-keep=.test %t %t2
+# RUN: llvm-objcopy -j=.test -keep=.test2 %t %t3
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+# RUN: diff %t2 %t3
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+  - Name:            .test2
+    Type:            SHT_PROGBITS
+  - Name:            .test3
+    Type:            SHT_PROGBITS
+
+# CHECK: SectionHeaderCount: 6
+
+# CHECK:     Name: .test
+# CHECK:     Name: .test2
+# CHECK:     Name: .symtab
+# CHECK:     Name: .strtab
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/only-keep-many.test b/test/tools/llvm-objcopy/only-keep-many.test
new file mode 100644 (file)
index 0000000..2f95623
--- /dev/null
@@ -0,0 +1,28 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -j .test1 -j .test2 %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test1
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+  - Name:            .test2
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+  - Name:            .test3
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 6
+
+# CHECK:     Name: .test1
+# CHECK:     Name: .test2
+# CHECK:     Name: .symtab
+# CHECK:     Name: .strtab
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/only-keep-remove-strtab.test b/test/tools/llvm-objcopy/only-keep-remove-strtab.test
new file mode 100644 (file)
index 0000000..c8946d3
--- /dev/null
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -R .symtab -R .strtab -only-keep=.test %t %t2
+# RUN: llvm-objcopy -j=.test -R .strtab -R .symtab %t %t3
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+# RUN: diff %t2 %t3
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 3
+
+# CHECK:     Name: .test
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/only-keep-strip-non-alloc.test b/test/tools/llvm-objcopy/only-keep-strip-non-alloc.test
new file mode 100644 (file)
index 0000000..bad0022
--- /dev/null
@@ -0,0 +1,19 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -strip-non-alloc -only-keep=.test %t %t2
+# RUN: llvm-readobj -file-headers -sections %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+
+# CHECK: SectionHeaderCount: 3
+
+# CHECK:     Name: .test
+# CHECK:     Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/strip-sections-keep.test b/test/tools/llvm-objcopy/strip-sections-keep.test
new file mode 100644 (file)
index 0000000..dcf6968
--- /dev/null
@@ -0,0 +1,13 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -strip-sections -keep=.shstrtab %t %t2
+# RUN: od -Ax -t c %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+
+# CHECK: \0 . s h s t r t a b \0
diff --git a/test/tools/llvm-objcopy/strip-sections-only-keep.test b/test/tools/llvm-objcopy/strip-sections-only-keep.test
new file mode 100644 (file)
index 0000000..2c9400c
--- /dev/null
@@ -0,0 +1,20 @@
+# RUN: yaml2obj %s > %t
+# RUN: llvm-objcopy -strip-sections -only-keep=.test %t %t2
+# RUN: od -Ax -t x1 %t2 | FileCheck %s
+# RUN: od -Ax -t c  %t2 | FileCheck %s -check-prefix=TEXT
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_REL
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .test
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Content:        "DEADBEEF"
+
+# CHECK: de ad be ef
+
+# TEXT-NOT: t e s t
index f12e6da7d21cdc6d8e07c3e633c10b48ad5726f5..9f98c04ad9bb92b166ee02833741e5cf9e44476b 100644 (file)
@@ -196,6 +196,7 @@ public:
                  SectionBase *DefinedIn, uint64_t Value, uint16_t Shndx,
                  uint64_t Sz);
   void addSymbolNames();
+  const SectionBase *getStrTab() const { return SymbolNames; }
   const Symbol *getSymbolByIndex(uint32_t Index) const;
   void removeSectionReferences(const SectionBase *Sec) override;
   void initialize(SectionTableRef SecTable) override;
@@ -368,6 +369,7 @@ public:
   Object(const object::ELFObjectFile<ELFT> &Obj);
   virtual ~Object() = default;
 
+  const SymbolTableSection *getSymTab() const { return SymbolTable; }
   const SectionBase *getSectionHeaderStrTab() const { return SectionNames; }
   void removeSections(std::function<bool(const SectionBase &)> ToRemove);
   virtual size_t totalSize() const = 0;
index 09553e85202d87df94bfaadf5ddfb555e0c5b8c8..615667bf99abf6d656e573f1deee27d95ef9f348 100644 (file)
@@ -89,6 +89,13 @@ static cl::opt<bool> StripAll(
 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 @@ void SplitDWOToFile(const ELFObjectFile<ELFT> &ObjFile, StringRef File) {
   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 @@ void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
 
   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 @@ void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
       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());