[llvm-objcopy]Don't implicitly strip sections in segments

This patch changes llvm-objcopy's behaviour to not strip sections that
are in segments, if they otherwise would be due to a stripping operation
(--strip-all, --strip-sections, --strip-non-alloc). This preserves the
segment contents. It does not change the behaviour of --strip-all-gnu
(although we could choose to do so), because GNU objcopy's behaviour in
this case seems to be to strip the section, nor does it prevent removing
of sections in segments with --remove-section (if a user REALLY wants to
remove a section, we should probably let them, although I could be
persuaded that warning might be appropriate). Tests have been added to
show this latter behaviour.

This fixes https://bugs.llvm.org/show_bug.cgi?id=41006.

Reviewed by: grimar, rupprecht, jakehehrlich

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

This is a reland of r356129, attempting to fix greendragon failures
due to a suspected compatibility issue with od on the greendragon bots
versus other versions.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@356136 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/tools/llvm-objcopy/ELF/remove-section-in-segment.test b/test/tools/llvm-objcopy/ELF/remove-section-in-segment.test
new file mode 100644
index 0000000..036c954
--- /dev/null
+++ b/test/tools/llvm-objcopy/ELF/remove-section-in-segment.test
@@ -0,0 +1,47 @@
+# Show that --remove-section removes sections in segments.
+
+# RUN: yaml2obj %s -o %t
+
+# Validate the offsets for the later parts of the test.
+# RUN: llvm-objcopy %t %t.copy
+# Skip to the start of the sections, which should be immediately after the
+# program header table (i.e. sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) == 120).
+# RUN: od -t x1 -N 16 -j 120 %t.copy | FileCheck %s --check-prefix=COPY
+
+# COPY: 11 22 33 44 55 66 77 88
+
+# RUN: llvm-objcopy --remove-section non_alloc %t %t.nonalloc
+# RUN: llvm-readelf --sections %t.nonalloc | FileCheck %s --check-prefix=NONALLOC-SHDRS
+# RUN: od -t x1 -N 16 -j 120 %t.nonalloc | FileCheck %s --check-prefix=NONALLOC-BYTES
+
+# NONALLOC-SHDRS-NOT: non_alloc
+# NONALLOC-BYTES: 11 22 33 44 00 00 00 00
+
+# RUN: llvm-objcopy --remove-section shf_alloc %t %t.alloc
+# RUN: llvm-readelf --sections %t.alloc | FileCheck %s --check-prefix=ALLOC-SHDRS
+# RUN: od -t x1 -N 16 -j 120 %t.alloc | FileCheck %s --check-prefix=ALLOC-BYTES
+
+# ALLOC-SHDRS-NOT: shf_alloc
+# ALLOC-BYTES: 00 00 00 00 55 66 77 88
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name:    shf_alloc
+    Type:    SHT_PROGBITS
+    Flags:   [SHF_ALLOC]
+    Content: '11223344'
+  - Name:    non_alloc
+    Type:    SHT_PROGBITS
+    Flags:   []
+    Content: '55667788'
+ProgramHeaders:
+  # Use an arbitrary segment type to show that the segment type is unimportant.
+  - Type: 0x61234567
+    Sections:
+      - Section: shf_alloc
+      - Section: non_alloc
diff --git a/test/tools/llvm-objcopy/ELF/strip-all-gnu.test b/test/tools/llvm-objcopy/ELF/strip-all-gnu.test
index 8253526..ea2cb63 100644
--- a/test/tools/llvm-objcopy/ELF/strip-all-gnu.test
+++ b/test/tools/llvm-objcopy/ELF/strip-all-gnu.test
@@ -1,10 +1,15 @@
 # RUN: yaml2obj %s > %t
-# RUN: cp %t %t1
 # RUN: llvm-objcopy --strip-all-gnu %t %t2
 # RUN: llvm-strip --strip-all-gnu %t -o %t3
 # RUN: llvm-readobj --file-headers --sections %t2 | FileCheck %s
 # RUN: cmp %t2 %t3
 
+# Show that the debug section in a segment was removed, to match GNU.
+# First validate that the offset in use is correct.
+# RUN: llvm-objcopy %t %t4
+# RUN: od -t x1 -N 4 -j 120 %t4 | FileCheck %s --check-prefix=COPY-BYTES
+# RUN: od -t x1 -N 4 -j 120 %t2 | FileCheck %s --check-prefix=STRIP-BYTES
+
 !ELF
 FileHeader:
   Class:           ELFCLASS64
@@ -12,6 +17,10 @@
   Type:            ET_REL
   Machine:         EM_X86_64
 Sections:
+  - Name:            .debug_in_segment
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Content:         "deadbeef"
   - Name:            .dynstr
     Type:            SHT_STRTAB
     Flags:           [ SHF_ALLOC ]
@@ -45,6 +54,11 @@
   - Name:            .rela.text
     Type:            SHT_RELA
     Info:            .text
+ProgramHeaders:
+  # Use an arbitrary segment type to show that the segment type is unimportant.
+  - Type: 0x61234567
+    Sections:
+      - Section: .debug_in_segment
 
 # CHECK: SectionHeaderCount: 8
 
@@ -55,3 +69,6 @@
 # CHECK:      Name: .comment
 # CHECK:      Name: .random_section_name
 # CHECK:      Name: .shstrtab
+
+# COPY-BYTES:  de ad be ef
+# STRIP-BYTES: 00 00 00 00
diff --git a/test/tools/llvm-objcopy/ELF/strip-all.test b/test/tools/llvm-objcopy/ELF/strip-all.test
index 87388e0..32a801d 100644
--- a/test/tools/llvm-objcopy/ELF/strip-all.test
+++ b/test/tools/llvm-objcopy/ELF/strip-all.test
@@ -57,6 +57,10 @@
   Type:            ET_REL
   Machine:         EM_X86_64
 Sections:
+  - Name:            non_alloc_in_segment
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Size:            4
   - Name:            .bss
     Type:            SHT_NOBITS
     Flags:           [ SHF_ALLOC ]
@@ -68,9 +72,15 @@
     Flags:           [ ]
   - Name:            .gnu.warning.foo
     Type:            SHT_PROGBITS
+ProgramHeaders:
+  # Use an arbitrary segment type to show that the segment type is unimportant.
+  - Type: 0x61234567
+    Sections:
+      - Section: non_alloc_in_segment
 
-# CHECK: SectionHeaderCount: 5
+# CHECK: SectionHeaderCount: 6
 
+# CHECK: Name: non_alloc_in_segment
 # CHECK: Name: .bss
 # CHECK: Name: .text
 # CHECK: Name: .gnu.warning.foo
diff --git a/test/tools/llvm-objcopy/ELF/strip-non-alloc.test b/test/tools/llvm-objcopy/ELF/strip-non-alloc.test
index 8db7321..1f082bf 100644
--- a/test/tools/llvm-objcopy/ELF/strip-non-alloc.test
+++ b/test/tools/llvm-objcopy/ELF/strip-non-alloc.test
@@ -9,6 +9,10 @@
   Type:            ET_REL
   Machine:         EM_X86_64
 Sections:
+  - Name:            non_alloc_in_segment
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Size:            4
   - Name:            .bss
     Type:            SHT_NOBITS
     Flags:           [ SHF_ALLOC ]
@@ -18,9 +22,15 @@
   - Name:            .blarg
     Type:            SHT_PROGBITS
     Flags:           [ ]
+ProgramHeaders:
+  # Use an arbitrary segment type to show that the segment type is unimportant.
+  - Type: 0x61234567
+    Sections:
+      - Section: non_alloc_in_segment
 
-# CHECK: SectionHeaderCount: 4
+# CHECK: SectionHeaderCount: 5
 
+# CHECK: Name: non_alloc_in_segment
 # CHECK: Name: .bss
 # CHECK: Name: .text
 # CHECK: Name: .shstrtab
diff --git a/test/tools/llvm-objcopy/ELF/strip-sections.test b/test/tools/llvm-objcopy/ELF/strip-sections.test
index 2dee6e2..244cdb3 100644
--- a/test/tools/llvm-objcopy/ELF/strip-sections.test
+++ b/test/tools/llvm-objcopy/ELF/strip-sections.test
@@ -15,13 +15,18 @@
     Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
     AddressAlign:    0x0000000000001000
     Content:         "DEADBEEF"
+  - Name:            .non_alloc_in_segment
+    Type:            SHT_PROGBITS
+    Flags:           [ ]
+    Content:         "CAFEBABE"
 ProgramHeaders:
 - Type: PT_LOAD
   Flags: [ PF_X, PF_R ]
   Sections:
     - Section: .text
+    - Section: .non_alloc_in_segment
 
-#DATA: 0010000 de ad be ef
+#DATA: 0010000 de ad be ef ca fe ba be
 
 #CHECK: ElfHeader {
 #CHECK-NEXT:  Ident {
@@ -55,8 +60,8 @@
 #CHECK-NEXT:    Offset: 0x1000
 #CHECK-NEXT:    VirtualAddress: 0x0
 #CHECK-NEXT:    PhysicalAddress: 0x0
-#CHECK-NEXT:    FileSize: 4
-#CHECK-NEXT:    MemSize: 4
+#CHECK-NEXT:    FileSize: 8
+#CHECK-NEXT:    MemSize: 8
 #CHECK-NEXT:    Flags [ (0x5)
 #CHECK-NEXT:      PF_R (0x4)
 #CHECK-NEXT:      PF_X (0x1)
diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index cbf6a3d..250eeea 100644
--- a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -403,7 +403,7 @@
 
   if (Config.StripSections) {
     RemovePred = [RemovePred](const SectionBase &Sec) {
-      return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
+      return RemovePred(Sec) || Sec.ParentSegment == nullptr;
     };
   }
 
@@ -419,7 +419,7 @@
         return true;
       if (&Sec == Obj.SectionNames)
         return false;
-      return (Sec.Flags & SHF_ALLOC) == 0;
+      return (Sec.Flags & SHF_ALLOC) == 0 && Sec.ParentSegment == nullptr;
     };
 
   if (Config.StripAll)
@@ -430,6 +430,8 @@
         return false;
       if (StringRef(Sec.Name).startswith(".gnu.warning"))
         return false;
+      if (Sec.ParentSegment != nullptr)
+        return false;
       return (Sec.Flags & SHF_ALLOC) == 0;
     };
 
diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td
index 44fd01c..a5f7119 100644
--- a/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/tools/llvm-objcopy/ObjcopyOpts.td
@@ -103,10 +103,9 @@
          "rom, share, contents, merge, strings.">,
       MetaVarName<"section=flag1[,flag2,...]">;
 
-def strip_all
-    : Flag<["-", "--"], "strip-all">,
-      HelpText<
-          "Remove non-allocated sections other than .gnu.warning* sections">;
+def strip_all : Flag<["-", "--"], "strip-all">,
+                HelpText<"Remove non-allocated sections outside segments. "
+                         ".gnu.warning* sections are not removed">;
 def S : Flag<["-"], "S">, Alias<strip_all>;
 def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">,
                     HelpText<"Compatible with GNU objcopy's --strip-all">;
@@ -116,13 +115,15 @@
         HelpText<"Alias for --strip-debug">;
 def strip_dwo : Flag<["-", "--"], "strip-dwo">,
                 HelpText<"Remove all DWARF .dwo sections from file">;
-def strip_sections : Flag<["-", "--"], "strip-sections">,
-                     HelpText<"Remove all section headers">;
-def strip_non_alloc : Flag<["-", "--"], "strip-non-alloc">,
-                      HelpText<"Remove all non-allocated sections">;
+def strip_sections
+    : Flag<["-", "--"], "strip-sections">,
+      HelpText<"Remove all section headers and all sections not in segments">;
+def strip_non_alloc
+    : Flag<["-", "--"], "strip-non-alloc">,
+      HelpText<"Remove all non-allocated sections outside segments">;
 def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
                      HelpText<"Remove all symbols not needed by relocations">;
-defm strip_unneeded_symbol 
+defm strip_unneeded_symbol
     : Eq<"strip-unneeded-symbol",
          "Remove symbol <symbol> if it is not needed by relocations">,
       MetaVarName<"symbol">;
diff --git a/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td
index c3f3519..40b043a 100644
--- a/tools/llvm-objcopy/StripOpts.td
+++ b/tools/llvm-objcopy/StripOpts.td
@@ -31,10 +31,9 @@
                      HelpText<"Preserve access and modification timestamps">;
 def p : Flag<["-"], "p">, Alias<preserve_dates>;
 
-def strip_all
-    : Flag<["-", "--"], "strip-all">,
-      HelpText<
-          "Remove non-allocated sections other than .gnu.warning* sections">;
+def strip_all : Flag<["-", "--"], "strip-all">,
+                HelpText<"Remove non-allocated sections outside segments. "
+                         ".gnu.warning* sections are not removed">;
 def s : Flag<["-"], "s">, Alias<strip_all>;
 
 def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">,