https://gcc.gnu.org/g:9b4a7d907d90ba7b7787433ab66eaf6112c33ffb

commit r13-9023-g9b4a7d907d90ba7b7787433ab66eaf6112c33ffb
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Sat Sep 7 09:36:53 2024 +0200

    libiberty: Fix up > 64K section handling in 
simple_object_elf_copy_lto_debug_section [PR116614]
    
    cat abc.C
      #define A(n) struct T##n {} t##n;
      #define B(n) A(n##0) A(n##1) A(n##2) A(n##3) A(n##4) A(n##5) A(n##6) 
A(n##7) A(n##8) A(n##9)
      #define C(n) B(n##0) B(n##1) B(n##2) B(n##3) B(n##4) B(n##5) B(n##6) 
B(n##7) B(n##8) B(n##9)
      #define D(n) C(n##0) C(n##1) C(n##2) C(n##3) C(n##4) C(n##5) C(n##6) 
C(n##7) C(n##8) C(n##9)
      #define E(n) D(n##0) D(n##1) D(n##2) D(n##3) D(n##4) D(n##5) D(n##6) 
D(n##7) D(n##8) D(n##9)
      E(1) E(2) E(3)
      int main () { return 0; }
    ./xg++ -B ./ -o abc{.o,.C} -flto -flto-partition=1to1 -O2 -g 
-fdebug-types-section -c
    ./xgcc -B ./ -o abc{,.o} -flto -flto-partition=1to1 -O2
    (not included in testsuite as it takes a while to compile) FAILs with
    lto-wrapper: fatal error: Too many copied sections: Operation not supported
    compilation terminated.
    /usr/bin/ld: error: lto-wrapper failed
    collect2: error: ld returned 1 exit status
    
    The following patch fixes that.  Most of the 64K+ section support for
    reading and writing was already there years ago (and especially reading used
    quite often already) and a further bug fixed in it in the PR104617 fix.
    
    Yet, the fix isn't solely about removing the
      if (new_i - 1 >= SHN_LORESERVE)
        {
          *err = ENOTSUP;
          return "Too many copied sections";
        }
    5 lines, the missing part was that the function only handled reading of
    the .symtab_shndx section but not copying/updating of it.
    If the result has less than 64K-epsilon sections, that actually wasn't
    needed, but e.g. with -fdebug-types-section one can exceed that pretty
    easily (reported to us on WebKitGtk build on ppc64le).
    Updating the section is slightly more complicated, because it basically
    needs to be done in lock step with updating the .symtab section, if one
    doesn't need to use SHN_XINDEX in there, the section should (or should be
    updated to) contain SHN_UNDEF entry, otherwise needs to have whatever would
    be overwise stored but couldn't fit.  But repeating due to that all the
    symtab decisions what to discard and how to rewrite it would be ugly.
    
    So, the patch instead emits the .symtab_shndx section (or sections) last
    and prepares the content during the .symtab processing and in a second
    pass when going just through .symtab_shndx sections just uses the saved
    content.
    
    2024-09-07  Jakub Jelinek  <ja...@redhat.com>
    
            PR lto/116614
            * simple-object-elf.c (SHN_COMMON): Align comment with neighbouring
            comments.
            (SHN_HIRESERVE): Use uppercase hex digits instead of lowercase for
            consistency.
            (simple_object_elf_find_sections): Formatting fixes.
            (simple_object_elf_fetch_attributes): Likewise.
            (simple_object_elf_attributes_merge): Likewise.
            (simple_object_elf_start_write): Likewise.
            (simple_object_elf_write_ehdr): Likewise.
            (simple_object_elf_write_shdr): Likewise.
            (simple_object_elf_write_to_file): Likewise.
            (simple_object_elf_copy_lto_debug_section): Likewise.  Don't fail 
for
            new_i - 1 >= SHN_LORESERVE, instead arrange in that case to copy
            over .symtab_shndx sections, though emit those last and compute 
their
            section content when processing associated .symtab sections.  Handle
            simple_object_internal_read failure even in the .symtab_shndx 
reading
            case.
    
    (cherry picked from commit bb8dd0980b39cfd601f88703fd356055727ef24d)

Diff:
---
 libiberty/simple-object-elf.c | 210 ++++++++++++++++++++++++++++--------------
 1 file changed, 143 insertions(+), 67 deletions(-)

diff --git a/libiberty/simple-object-elf.c b/libiberty/simple-object-elf.c
index eee07039984d..501b5ba62aac 100644
--- a/libiberty/simple-object-elf.c
+++ b/libiberty/simple-object-elf.c
@@ -128,9 +128,9 @@ typedef struct {
 
 #define SHN_UNDEF      0               /* Undefined section */
 #define SHN_LORESERVE  0xFF00          /* Begin range of reserved indices */
-#define SHN_COMMON     0xFFF2  /* Associated symbol is in common */
+#define SHN_COMMON     0xFFF2          /* Associated symbol is in common */
 #define SHN_XINDEX     0xFFFF          /* Section index is held elsewhere */
-#define SHN_HIRESERVE  0xffff          /* End of reserved indices */
+#define SHN_HIRESERVE  0xFFFF          /* End of reserved indices */
 
 
 /* 32-bit ELF program header.  */
@@ -569,8 +569,8 @@ simple_object_elf_find_sections (simple_object_read *sobj,
                                 void *data,
                                 int *err)
 {
-  struct simple_object_elf_read *eor =
-    (struct simple_object_elf_read *) sobj->data;
+  struct simple_object_elf_read *eor
+    = (struct simple_object_elf_read *) sobj->data;
   const struct elf_type_functions *type_functions = eor->type_functions;
   unsigned char ei_class = eor->ei_class;
   size_t shdr_size;
@@ -662,8 +662,8 @@ simple_object_elf_fetch_attributes (simple_object_read 
*sobj,
                                    const char **errmsg ATTRIBUTE_UNUSED,
                                    int *err ATTRIBUTE_UNUSED)
 {
-  struct simple_object_elf_read *eor =
-    (struct simple_object_elf_read *) sobj->data;
+  struct simple_object_elf_read *eor
+    = (struct simple_object_elf_read *) sobj->data;
   struct simple_object_elf_attributes *ret;
 
   ret = XNEW (struct simple_object_elf_attributes);
@@ -689,10 +689,10 @@ simple_object_elf_release_read (void *data)
 static const char *
 simple_object_elf_attributes_merge (void *todata, void *fromdata, int *err)
 {
-  struct simple_object_elf_attributes *to =
-    (struct simple_object_elf_attributes *) todata;
-  struct simple_object_elf_attributes *from =
-    (struct simple_object_elf_attributes *) fromdata;
+  struct simple_object_elf_attributes *to
+    = (struct simple_object_elf_attributes *) todata;
+  struct simple_object_elf_attributes *from
+    = (struct simple_object_elf_attributes *) fromdata;
 
   if (to->ei_data != from->ei_data || to->ei_class != from->ei_class)
     {
@@ -751,8 +751,8 @@ simple_object_elf_start_write (void *attributes_data,
                               const char **errmsg ATTRIBUTE_UNUSED,
                               int *err ATTRIBUTE_UNUSED)
 {
-  struct simple_object_elf_attributes *attrs =
-    (struct simple_object_elf_attributes *) attributes_data;
+  struct simple_object_elf_attributes *attrs
+    = (struct simple_object_elf_attributes *) attributes_data;
   struct simple_object_elf_write *ret;
 
   /* We're just going to record the attributes, but we need to make a
@@ -769,8 +769,8 @@ static int
 simple_object_elf_write_ehdr (simple_object_write *sobj, int descriptor,
                              const char **errmsg, int *err)
 {
-  struct simple_object_elf_attributes *attrs =
-    (struct simple_object_elf_attributes *) sobj->data;
+  struct simple_object_elf_attributes *attrs
+    = (struct simple_object_elf_attributes *) sobj->data;
   const struct elf_type_functions* fns;
   unsigned char cl;
   size_t ehdr_size;
@@ -852,8 +852,8 @@ simple_object_elf_write_shdr (simple_object_write *sobj, 
int descriptor,
                              size_t sh_entsize,
                              const char **errmsg, int *err)
 {
-  struct simple_object_elf_attributes *attrs =
-    (struct simple_object_elf_attributes *) sobj->data;
+  struct simple_object_elf_attributes *attrs
+    = (struct simple_object_elf_attributes *) sobj->data;
   const struct elf_type_functions* fns;
   unsigned char cl;
   size_t shdr_size;
@@ -894,8 +894,8 @@ static const char *
 simple_object_elf_write_to_file (simple_object_write *sobj, int descriptor,
                                 int *err)
 {
-  struct simple_object_elf_write *eow =
-    (struct simple_object_elf_write *) sobj->data;
+  struct simple_object_elf_write *eow
+    = (struct simple_object_elf_write *) sobj->data;
   struct simple_object_elf_attributes *attrs = &eow->attrs;
   unsigned char cl;
   size_t ehdr_size;
@@ -1088,11 +1088,11 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
                                           char *(*pfn) (const char *),
                                           int *err)
 {
-  struct simple_object_elf_read *eor =
-    (struct simple_object_elf_read *) sobj->data;
+  struct simple_object_elf_read *eor
+    = (struct simple_object_elf_read *) sobj->data;
   const struct elf_type_functions *type_functions = eor->type_functions;
-  struct simple_object_elf_write *eow =
-    (struct simple_object_elf_write *) dobj->data;
+  struct simple_object_elf_write *eow
+    = (struct simple_object_elf_write *) dobj->data;
   unsigned char ei_class = eor->ei_class;
   size_t shdr_size;
   unsigned int shnum;
@@ -1106,10 +1106,13 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
   int changed;
   int *pfnret;
   const char **pfnname;
-  unsigned new_i;
+  unsigned new_i, new_count;
   unsigned *sh_map;
   unsigned first_shndx = 0;
   unsigned int *symtab_indices_shndx;
+  int pass_symtab_indices_shndx;
+  unsigned int first_symtab_indices_shndx;
+  unsigned char **symtab_indices_shndx_buf;
 
   shdr_size = (ei_class == ELFCLASS32
               ? sizeof (Elf32_External_Shdr)
@@ -1179,8 +1182,7 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
       ret = (*pfn) (name);
       pfnret[i - 1] = ret == NULL ? -1 : 0;
       pfnname[i - 1] = ret == NULL ? name : ret;
-      if (first_shndx == 0
-         && pfnret[i - 1] == 0)
+      if (first_shndx == 0 && pfnret[i - 1] == 0)
        first_shndx = i;
 
       /* Remember the indexes of existing SHT_SYMTAB_SHNDX sections.  */
@@ -1191,11 +1193,12 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
          unsigned int sh_link;
          sh_link = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
                                     shdr, sh_link, Elf_Word);
-         symtab_indices_shndx[sh_link - 1] = i - 1;
-         /* Always discard the extended index sections, after
-            copying it will not be needed.  This way we don't need to
-            update it and deal with the ordering constraints of
-            processing the existing symtab and changing the index.  */
+         symtab_indices_shndx[sh_link - 1] = i;
+         /* Discard the extended index sections, after copying it will not
+            be needed, unless we need more than SHN_LORESERVE - 1 sections
+            in the output.  This way we don't need to update it and deal with
+            the ordering constraints of processing the existing symtab and
+            changing the index.  */
          pfnret[i - 1] = -1;
        }
     }
@@ -1291,16 +1294,25 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
       else
        sh_map[i] = new_i++;
     }
+  first_symtab_indices_shndx = new_i;
+  symtab_indices_shndx_buf = NULL;
   if (new_i - 1 >= SHN_LORESERVE)
-    {
-      *err = ENOTSUP;
-      return "Too many copied sections";
-    }
-  eow->shdrs = XNEWVEC (unsigned char, shdr_size * (new_i - 1));
+    for (i = 1; i < shnum; ++i)
+      if (pfnret[i - 1] == 0 && symtab_indices_shndx[i - 1] != 0)
+       {
+         pfnret[symtab_indices_shndx[i - 1] - 1] = 0;
+         sh_map[symtab_indices_shndx[i - 1]] = new_i++;
+       }
+  new_count = new_i;
+  if (new_count != first_symtab_indices_shndx)
+    symtab_indices_shndx_buf
+      = XNEWVEC (unsigned char *, new_count - first_symtab_indices_shndx);
+  eow->shdrs = XNEWVEC (unsigned char, shdr_size * (new_count - 1));
 
   /* Then perform the actual copying.  */
   new_i = 0;
-  for (i = 1; i < shnum; ++i)
+  pass_symtab_indices_shndx = 0;
+  for (i = 1; i <= shnum; ++i)
     {
       unsigned char *shdr;
       unsigned int sh_name, sh_type;
@@ -1311,11 +1323,30 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
       off_t flags;
       unsigned char *buf;
 
+      if (i == shnum)
+       {
+         if (new_count - 1 < SHN_LORESERVE || pass_symtab_indices_shndx)
+           break;
+         i = 0;
+         pass_symtab_indices_shndx = 1;
+         continue;
+       }
+
       if (pfnret[i - 1])
        continue;
 
-      new_i++;
       shdr = shdrs + (i - 1) * shdr_size;
+      sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+                                shdr, sh_type, Elf_Word);
+      if (sh_type == SHT_SYMTAB_SHNDX)
+       {
+         if (!pass_symtab_indices_shndx)
+           continue;
+       }
+      else if (pass_symtab_indices_shndx)
+       continue;
+
+      new_i++;
       sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
                                 shdr, sh_name, Elf_Word);
       if (sh_name >= name_size)
@@ -1324,6 +1355,7 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
          XDELETEVEC (names);
          XDELETEVEC (shdrs);
          XDELETEVEC (symtab_indices_shndx);
+         XDELETEVEC (symtab_indices_shndx_buf);
          return "ELF section name out of range";
        }
 
@@ -1332,16 +1364,14 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
                                shdr, sh_offset, Elf_Addr);
       length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
                                shdr, sh_size, Elf_Addr);
-      sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
-                                shdr, sh_type, Elf_Word);
 
-      dest = simple_object_write_create_section (dobj, pfnname[i - 1],
-                                                0, &errmsg, err);
+      dest = simple_object_write_create_section (dobj, name, 0, &errmsg, err);
       if (dest == NULL)
        {
          XDELETEVEC (names);
          XDELETEVEC (shdrs);
          XDELETEVEC (symtab_indices_shndx);
+         XDELETEVEC (symtab_indices_shndx_buf);
          return errmsg;
        }
 
@@ -1363,6 +1393,7 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
          XDELETEVEC (names);
          XDELETEVEC (shdrs);
          XDELETEVEC (symtab_indices_shndx);
+         XDELETEVEC (symtab_indices_shndx_buf);
          return errmsg;
        }
 
@@ -1378,7 +1409,8 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
          /* Read the section index table if present.  */
          if (symtab_indices_shndx[i - 1] != 0)
            {
-             unsigned char *sidxhdr = shdrs + symtab_indices_shndx[i - 1] * 
shdr_size;
+             unsigned char *sidxhdr
+               = shdrs + (symtab_indices_shndx[i - 1] - 1) * shdr_size;
              off_t sidxoff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
                                               sidxhdr, sh_offset, Elf_Addr);
              size_t sidxsz = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
@@ -1388,11 +1420,20 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
                                   sidxhdr, sh_type, Elf_Word);
              if (shndx_type != SHT_SYMTAB_SHNDX)
                return "Wrong section type of a SYMTAB SECTION INDICES section";
-             shndx_table = (unsigned *)XNEWVEC (char, sidxsz);
-             simple_object_internal_read (sobj->descriptor,
-                                          sobj->offset + sidxoff,
-                                          (unsigned char *)shndx_table,
-                                          sidxsz, &errmsg, err);
+             shndx_table = (unsigned *) XNEWVEC (char, sidxsz);
+             if (!simple_object_internal_read (sobj->descriptor,
+                                               sobj->offset + sidxoff,
+                                               (unsigned char *) shndx_table,
+                                               sidxsz, &errmsg, err))
+               {
+                 XDELETEVEC (buf);
+                 XDELETEVEC (names);
+                 XDELETEVEC (shdrs);
+                 XDELETEVEC (symtab_indices_shndx);
+                 XDELETEVEC (shndx_table);
+                 XDELETEVEC (symtab_indices_shndx_buf);
+                 return errmsg;
+               }
            }
 
          /* Find a WEAK HIDDEN symbol which name we will use for removed
@@ -1407,17 +1448,20 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
              unsigned char *st_other;
              if (ei_class == ELFCLASS32)
                {
-                 st_info = &((Elf32_External_Sym *)ent)->st_info;
-                 st_other = &((Elf32_External_Sym *)ent)->st_other;
+                 st_info = &((Elf32_External_Sym *) ent)->st_info;
+                 st_other = &((Elf32_External_Sym *) ent)->st_other;
                }
              else
                {
-                 st_info = &((Elf64_External_Sym *)ent)->st_info;
-                 st_other = &((Elf64_External_Sym *)ent)->st_other;
+                 st_info = &((Elf64_External_Sym *) ent)->st_info;
+                 st_other = &((Elf64_External_Sym *) ent)->st_other;
                }
              if (st_shndx == SHN_XINDEX)
-               st_shndx = type_functions->fetch_Elf_Word
-                   ((unsigned char *)(shndx_table + (ent - buf) / entsize));
+               {
+                 unsigned char *ndx_ptr
+                   = (unsigned char *) (shndx_table + (ent - buf) / entsize);
+                 st_shndx = type_functions->fetch_Elf_Word (ndx_ptr);
+               }
 
              if (st_shndx != SHN_COMMON
                  && !(st_shndx != SHN_UNDEF
@@ -1442,19 +1486,26 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
              unsigned char *st_info;
              unsigned char *st_other;
              int discard = 0;
+             unsigned char *ndx_ptr = NULL;
              if (ei_class == ELFCLASS32)
                {
-                 st_info = &((Elf32_External_Sym *)ent)->st_info;
-                 st_other = &((Elf32_External_Sym *)ent)->st_other;
+                 st_info = &((Elf32_External_Sym *) ent)->st_info;
+                 st_other = &((Elf32_External_Sym *) ent)->st_other;
                }
              else
                {
-                 st_info = &((Elf64_External_Sym *)ent)->st_info;
-                 st_other = &((Elf64_External_Sym *)ent)->st_other;
+                 st_info = &((Elf64_External_Sym *) ent)->st_info;
+                 st_other = &((Elf64_External_Sym *) ent)->st_other;
                }
+             if (shndx_table)
+               ndx_ptr
+                 = (unsigned char *) (shndx_table + (ent - buf) / entsize);
+
              if (st_shndx == SHN_XINDEX)
-               st_shndx = type_functions->fetch_Elf_Word
-                   ((unsigned char *)(shndx_table + (ent - buf) / entsize));
+               {
+                 st_shndx = type_functions->fetch_Elf_Word (ndx_ptr);
+                 type_functions->set_Elf_Word (ndx_ptr, SHN_UNDEF);
+               }
              /* Eliminate all COMMONs - this includes __gnu_lto_slim
                 which otherwise cause endless LTO plugin invocation.
                 FIXME: remove the condition once we remove emission
@@ -1488,9 +1539,14 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
                         defined in the first prevailing section.  */
                      ELF_SET_FIELD (type_functions, ei_class, Sym,
                                     ent, st_name, Elf_Word, 0);
+                     st_shndx = sh_map[first_shndx];
+                     if (st_shndx >= SHN_LORESERVE)
+                       {
+                         type_functions->set_Elf_Word (ndx_ptr, st_shndx);
+                         st_shndx = SHN_XINDEX;
+                       }
                      ELF_SET_FIELD (type_functions, ei_class, Sym,
-                                    ent, st_shndx, Elf_Half,
-                                    sh_map[first_shndx]);
+                                    ent, st_shndx, Elf_Half, st_shndx);
                    }
                  else
                    {
@@ -1514,11 +1570,24 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
                }
              else if (raw_st_shndx < SHN_LORESERVE
                       || raw_st_shndx == SHN_XINDEX)
-               /* Remap the section reference.  */
-               ELF_SET_FIELD (type_functions, ei_class, Sym,
-                              ent, st_shndx, Elf_Half, sh_map[st_shndx]);
+               {
+                 /* Remap the section reference.  */
+                 st_shndx = sh_map[st_shndx];
+                 if (st_shndx >= SHN_LORESERVE)
+                   {
+                     type_functions->set_Elf_Word (ndx_ptr, st_shndx);
+                     st_shndx = SHN_XINDEX;
+                   }
+                 ELF_SET_FIELD (type_functions, ei_class, Sym,
+                                ent, st_shndx, Elf_Half, st_shndx);
+               }
            }
-         XDELETEVEC (shndx_table);
+         if (symtab_indices_shndx_buf)
+           symtab_indices_shndx_buf[sh_map[symtab_indices_shndx[i - 1]]
+                                    - first_symtab_indices_shndx]
+             = (unsigned char *) shndx_table;
+         else
+           XDELETEVEC (shndx_table);
        }
       else if (sh_type == SHT_GROUP)
        {
@@ -1538,15 +1607,21 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
          /* Adjust the length.  */
          length = dst - buf;
        }
+      else if (sh_type == SHT_SYMTAB_SHNDX)
+       {
+         XDELETEVEC (buf);
+         buf = symtab_indices_shndx_buf[new_i - first_symtab_indices_shndx];
+         symtab_indices_shndx_buf[new_i - first_symtab_indices_shndx] = NULL;
+       }
 
-      errmsg = simple_object_write_add_data (dobj, dest,
-                                            buf, length, 1, err);
+      errmsg = simple_object_write_add_data (dobj, dest, buf, length, 1, err);
       XDELETEVEC (buf);
       if (errmsg)
        {
          XDELETEVEC (names);
          XDELETEVEC (shdrs);
          XDELETEVEC (symtab_indices_shndx);
+         XDELETEVEC (symtab_indices_shndx_buf);
          return errmsg;
        }
 
@@ -1586,6 +1661,7 @@ simple_object_elf_copy_lto_debug_sections 
(simple_object_read *sobj,
   XDELETEVEC (pfnname);
   XDELETEVEC (symtab_indices_shndx);
   XDELETEVEC (sh_map);
+  XDELETEVEC (symtab_indices_shndx_buf);
 
   return NULL;
 }

Reply via email to