c59734b
commit c06ab0bbb4761a69d2f188675d21d1a9131e9ecb
c59734b
Author: Mark Wielaard <mark@klomp.org>
c59734b
Date:   Sat Oct 13 10:27:47 2018 +0200
c59734b
c59734b
    strip, unstrip: Handle SHT_GROUP correctly.
c59734b
    
c59734b
    The usage of annobin in Fedora showed a couple of bugs when using
c59734b
    eu-strip and eu-unstrip on ET_REL files that contain multiple group
c59734b
    sections.
c59734b
    
c59734b
    When stripping we should not remove the SHF_GROUP flag from sections
c59734b
    even if the group section itself might be removed. Either the section
c59734b
    itself gets removed, and so the flag doesn't matter. Or it gets moved
c59734b
    together with the group section into the debug file, and then it still
c59734b
    needs to have the flag set. Also we would "renumber" the section group
c59734b
    flag field (which isn't a section index, and so shouldn't be changed).
c59734b
    
c59734b
    Often the group sections have the exact same name (".group"), flags
c59734b
    (none) and sometimes the same sizes. Which makes matching them hard.
c59734b
    Extract the group signature and compare those when comparing two
c59734b
    group sections.
c59734b
    
c59734b
    Signed-off-by: Mark Wielaard <mark@klomp.org>
c59734b
c59734b
diff --git a/src/strip.c b/src/strip.c
c59734b
index 1f7b3ca..fdebc5e 100644
c59734b
--- a/src/strip.c
c59734b
+++ b/src/strip.c
c59734b
@@ -792,9 +792,13 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
c59734b
 
c59734b
 	  if (shdr_info[shdr_info[cnt].group_idx].idx == 0)
c59734b
 	    {
c59734b
-	      /* The section group section will be removed.  */
c59734b
+	      /* The section group section might be removed.
c59734b
+		 Don't remove the SHF_GROUP flag.  The section is
c59734b
+		 either also removed, in which case the flag doesn't matter.
c59734b
+		 Or it moves with the group into the debug file, then
c59734b
+		 it will be reconnected with the new group and should
c59734b
+		 still have the flag set.  */
c59734b
 	      shdr_info[cnt].group_idx = 0;
c59734b
-	      shdr_info[cnt].shdr.sh_flags &= ~SHF_GROUP;
c59734b
 	    }
c59734b
 	}
c59734b
 
c59734b
@@ -1368,7 +1372,9 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
c59734b
 			&& shdr_info[cnt].data->d_buf != NULL);
c59734b
 
c59734b
 	    Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf;
c59734b
-	    for (size_t inner = 0;
c59734b
+	    /* First word is the section group flag.
c59734b
+	       Followed by section indexes, that need to be renumbered.  */
c59734b
+	    for (size_t inner = 1;
c59734b
 		 inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word);
c59734b
 		 ++inner)
c59734b
 	      if (grpref[inner] < shnum)
c59734b
diff --git a/src/unstrip.c b/src/unstrip.c
c59734b
index e6f0947..03a0346 100644
c59734b
--- a/src/unstrip.c
c59734b
+++ b/src/unstrip.c
c59734b
@@ -696,6 +696,7 @@ struct section
c59734b
 {
c59734b
   Elf_Scn *scn;
c59734b
   const char *name;
c59734b
+  const char *sig;
c59734b
   Elf_Scn *outscn;
c59734b
   Dwelf_Strent *strent;
c59734b
   GElf_Shdr shdr;
c59734b
@@ -720,7 +721,8 @@ compare_alloc_sections (const struct section *s1, const struct section *s2,
c59734b
 
c59734b
 static int
c59734b
 compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2,
c59734b
-			  const char *name1, const char *name2)
c59734b
+			  const char *name1, const char *name2,
c59734b
+			  const char *sig1, const char *sig2)
c59734b
 {
c59734b
   /* Sort by sh_flags as an arbitrary ordering.  */
c59734b
   if (shdr1->sh_flags < shdr2->sh_flags)
c59734b
@@ -734,6 +736,10 @@ compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2,
c59734b
   if (shdr1->sh_size > shdr2->sh_size)
c59734b
     return 1;
c59734b
 
c59734b
+  /* Are they both SHT_GROUP sections? Then compare signatures.  */
c59734b
+  if (sig1 != NULL && sig2 != NULL)
c59734b
+    return strcmp (sig1, sig2);
c59734b
+
c59734b
   /* Sort by name as last resort.  */
c59734b
   return strcmp (name1, name2);
c59734b
 }
c59734b
@@ -751,7 +757,8 @@ compare_sections (const void *a, const void *b, bool rel)
c59734b
   return ((s1->shdr.sh_flags & SHF_ALLOC)
c59734b
 	  ? compare_alloc_sections (s1, s2, rel)
c59734b
 	  : compare_unalloc_sections (&s1->shdr, &s2->shdr,
c59734b
-				      s1->name, s2->name));
c59734b
+				      s1->name, s2->name,
c59734b
+				      s1->sig, s2->sig));
c59734b
 }
c59734b
 
c59734b
 static int
c59734b
@@ -986,6 +993,44 @@ get_section_name (size_t ndx, const GElf_Shdr *shdr, const Elf_Data *shstrtab)
c59734b
   return shstrtab->d_buf + shdr->sh_name;
c59734b
 }
c59734b
 
c59734b
+/* Returns the signature of a group section, or NULL if the given
c59734b
+   section isn't a group.  */
c59734b
+static const char *
c59734b
+get_group_sig (Elf *elf, GElf_Shdr *shdr)
c59734b
+{
c59734b
+  if (shdr->sh_type != SHT_GROUP)
c59734b
+    return NULL;
c59734b
+
c59734b
+  Elf_Scn *symscn = elf_getscn (elf, shdr->sh_link);
c59734b
+  if (symscn == NULL)
c59734b
+    error (EXIT_FAILURE, 0, _("bad sh_link for group section: %s"),
c59734b
+	   elf_errmsg (-1));
c59734b
+
c59734b
+  GElf_Shdr symshdr_mem;
c59734b
+  GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
c59734b
+  if (symshdr == NULL)
c59734b
+    error (EXIT_FAILURE, 0, _("couldn't get shdr for group section: %s"),
c59734b
+	   elf_errmsg (-1));
c59734b
+
c59734b
+  Elf_Data *symdata = elf_getdata (symscn, NULL);
c59734b
+  if (symdata == NULL)
c59734b
+    error (EXIT_FAILURE, 0, _("bad data for group symbol section: %s"),
c59734b
+	   elf_errmsg (-1));
c59734b
+
c59734b
+  GElf_Sym sym_mem;
c59734b
+  GElf_Sym *sym = gelf_getsym (symdata, shdr->sh_info, &sym_mem);
c59734b
+  if (sym == NULL)
c59734b
+    error (EXIT_FAILURE, 0, _("couldn't get symbol for group section: %s"),
c59734b
+	   elf_errmsg (-1));
c59734b
+
c59734b
+  const char *sig = elf_strptr (elf, symshdr->sh_link, sym->st_name);
c59734b
+  if (sig == NULL)
c59734b
+    error (EXIT_FAILURE, 0, _("bad symbol name for group section: %s"),
c59734b
+	   elf_errmsg (-1));
c59734b
+
c59734b
+  return sig;
c59734b
+}
c59734b
+
c59734b
 /* Fix things up when prelink has moved some allocated sections around
c59734b
    and the debuginfo file's section headers no longer match up.
c59734b
    This fills in SECTIONS[0..NALLOC-1].outscn or exits.
c59734b
@@ -1111,6 +1156,7 @@ find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab,
c59734b
 	      sec->scn = elf_getscn (main, i + 1); /* Really just for ndx.  */
c59734b
 	      sec->outscn = NULL;
c59734b
 	      sec->strent = NULL;
c59734b
+	      sec->sig = get_group_sig (main, &sec->shdr);
c59734b
 	      ++undo_nalloc;
c59734b
 	    }
c59734b
 	}
c59734b
@@ -1336,6 +1382,7 @@ more sections in stripped file than debug file -- arguments reversed?"));
c59734b
       sections[i].scn = scn;
c59734b
       sections[i].outscn = NULL;
c59734b
       sections[i].strent = NULL;
c59734b
+      sections[i].sig = get_group_sig (stripped, shdr);
c59734b
     }
c59734b
 
c59734b
   const struct section *stripped_symtab = NULL;
c59734b
@@ -1354,7 +1401,8 @@ more sections in stripped file than debug file -- arguments reversed?"));
c59734b
 
c59734b
   /* Locate a matching unallocated section in SECTIONS.  */
c59734b
   inline struct section *find_unalloc_section (const GElf_Shdr *shdr,
c59734b
-					       const char *name)
c59734b
+					       const char *name,
c59734b
+					       const char *sig)
c59734b
     {
c59734b
       size_t l = nalloc, u = stripped_shnum - 1;
c59734b
       while (l < u)
c59734b
@@ -1362,7 +1410,8 @@ more sections in stripped file than debug file -- arguments reversed?"));
c59734b
 	  size_t i = (l + u) / 2;
c59734b
 	  struct section *sec = &sections[i];
c59734b
 	  int cmp = compare_unalloc_sections (shdr, &sec->shdr,
c59734b
-					      name, sec->name);
c59734b
+					      name, sec->name,
c59734b
+					      sig, sec->sig);
c59734b
 	  if (cmp < 0)
c59734b
 	    u = i;
c59734b
 	  else if (cmp > 0)
c59734b
@@ -1435,7 +1484,8 @@ more sections in stripped file than debug file -- arguments reversed?"));
c59734b
       else
c59734b
 	{
c59734b
 	  /* Look for the section that matches.  */
c59734b
-	  sec = find_unalloc_section (shdr, name);
c59734b
+	  sec = find_unalloc_section (shdr, name,
c59734b
+				      get_group_sig (unstripped, shdr));
c59734b
 	  if (sec == NULL)
c59734b
 	    {
c59734b
 	      /* An additional unallocated section is fine if not SHT_NOBITS.