]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Don't read and cache local syms for gc-sections
authorAlan Modra <amodra@gmail.com>
Thu, 30 Oct 2025 05:56:50 +0000 (16:26 +1030)
committerAlan Modra <amodra@gmail.com>
Thu, 30 Oct 2025 05:56:50 +0000 (16:26 +1030)
Most places just need the local sym section, so reading and sometimes
caching the symbols is excessive.  A symbol shndx can be stored in 4
bytes, an elf symbol internal form requires 32 bytes.  When caching
the local symbols we went slightly crazy trying to avoid memory usage,
resulting in the symbols being freed then immediately read again for
the testcase in the PR33530.

To avoid this problem, this patch caches the local symbol section
indices in the bfd rather than in the reloc cookie.  They are not
initialised until there is a need for them, so unlike elf_sym_hashes
for global syms you cannot rely on them being present.

One place that does need local syms is adjust_eh_frame_local_symbols,
but that is called once via bfd_discard_info so there is no problem
simply reading them.  The other place that needs local syms is
ppc64_elf_gc_mark_hook for the old ELFv1 ABI when handling .opd.
bfd_sym_from_r_symndx should be sufficient for function pointer
references to static functions, which is how this code is triggered.

PR 33530
* elf-bfd.h (struct elf_reloc_cookie): Delete "locsyms",
"sym_hashes", "bad_symtab".  Make "locsymcount" and
"extsymoff" unsigned int.
(struct elf_obj_tdata): Add loc_shndx.
(elf_loc_shndx): Define.
(_bfd_get_local_sym_section): Declare.
* elf-eh-frame.c (find_merged_cie): Use
_bfd_get_local_sym_section for local syms.
(adjust_eh_frame_local_symbols): Read local syms if any match
.eh_frame section.  Return them if changed.
(_bfd_elf_discard_section_eh_frame): Adjust.
* elf64-ppc.c (ppc64_elf_gc_mark_hook): Use
_bfd_get_local_sym_section.  Use bfd_sym_from_r_symndx when
reading opd local symbol.
* elflink.c (_bfd_get_local_sym_section): New function.
(_bfd_elf_section_for_symbol): Use it.
(elf_link_add_object_symbols): Remove unnecessary cast on
bfd_zalloc return.
(init_reloc_cookie): Remove "info" and "keep_memory" params.
Adjust all callers.  Don't stash elf_sym_hashes and
elf_bad_symtab to cookie.  Don't read local syms to cookie.
(fini_reloc_cookie): Do nothing.
(_bfd_elf_gc_mark_hook): Use _bfd_get_local_sym_section.
(elf_gc_mark_debug_section): Likewise.
(bfd_elf_reloc_symbol_deleted_p): Likewise.  Update cookie use.

bfd/elf-bfd.h
bfd/elf-eh-frame.c
bfd/elf64-ppc.c
bfd/elflink.c

index dfadb3e933e3c1eac99067f63031b15a7d940a4a..e51bfcf63603ee9f613583cbb00252f43deb5255 100644 (file)
@@ -899,14 +899,13 @@ enum elf_reloc_type_class {
 
 struct elf_reloc_cookie
 {
-  Elf_Internal_Rela *rels, *rel, *relend;
-  Elf_Internal_Sym *locsyms;
   bfd *abfd;
-  size_t locsymcount;
-  size_t extsymoff;
-  struct elf_link_hash_entry **sym_hashes;
+  Elf_Internal_Rela *rels, *rel, *relend;
+  /* Number of symbols that may be local syms (all when bad_symtab).  */
+  unsigned int locsymcount;
+  /* Symbol index of first possible global sym (0 when bad_symtab).  */
+  unsigned int extsymoff;
   int r_sym_shift;
-  bool bad_symtab;
 };
 
 /* The level of IRIX compatibility we're striving for.  */
@@ -2105,6 +2104,9 @@ struct elf_obj_tdata
      minus the sh_info field of the symbol table header.  */
   struct elf_link_hash_entry **sym_hashes;
 
+  /* Section indices of local symbols, used by gc-sections.  */
+  unsigned int *loc_shndx;
+
   /* Track usage and final offsets of GOT entries for local symbols.
      This array is indexed by symbol index.  Elements are used
      identically to "got" in struct elf_link_hash_entry.  */
@@ -2244,6 +2246,7 @@ struct elf_obj_tdata
 #define elf_gp(bfd)            (elf_tdata(bfd) -> gp)
 #define elf_gp_size(bfd)       (elf_tdata(bfd) -> gp_size)
 #define elf_sym_hashes(bfd)    (elf_tdata(bfd) -> sym_hashes)
+#define elf_loc_shndx(bfd)     (elf_tdata(bfd) -> loc_shndx)
 #define elf_local_got_refcounts(bfd) (elf_tdata(bfd) -> local_got.refcounts)
 #define elf_local_got_offsets(bfd) (elf_tdata(bfd) -> local_got.offsets)
 #define elf_local_got_ents(bfd) (elf_tdata(bfd) -> local_got.ents)
@@ -3170,6 +3173,8 @@ extern void _bfd_elf_link_munmap_section_contents
 
 extern struct elf_link_hash_entry * _bfd_elf_get_link_hash_entry
   (struct elf_link_hash_entry **, unsigned int, unsigned int);
+extern asection *_bfd_get_local_sym_section
+  (struct elf_reloc_cookie *, unsigned int);
 
 /* Large common section.  */
 extern asection _bfd_elf_large_com_section;
index 87cf45a0db039d4c684c4a20bcf1c88fdf091676..97ba1a3dce69db0210541b82b17c15be062abf91 100644 (file)
@@ -1271,11 +1271,7 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
        }
       else
        {
-         Elf_Internal_Sym *sym;
-         asection *sym_sec;
-
-         sym = &cookie->locsyms[r_symndx];
-         sym_sec = bfd_section_from_elf_index (abfd, sym->st_shndx);
+         asection *sym_sec = _bfd_get_local_sym_section (cookie, r_symndx);
          if (sym_sec == NULL)
            return cie_inf;
 
@@ -1444,35 +1440,54 @@ _bfd_elf_adjust_eh_frame_global_symbol (struct elf_link_hash_entry *h,
   return true;
 }
 
-/* The same for all local symbols defined in .eh_frame.  Returns true
-   if any symbol was changed.  */
+/* The same for all local symbols defined in .eh_frame.  Returns the
+   local symbols if any symbol was changed.  */
 
-static int
+static Elf_Internal_Sym *
 adjust_eh_frame_local_symbols (const asection *sec,
                               struct elf_reloc_cookie *cookie)
 {
-  int adjusted = 0;
+  bfd *abfd = cookie->abfd;
+  unsigned int *loc_shndx = elf_loc_shndx (abfd);
+  unsigned int shndx = elf_section_data (sec)->this_idx;
 
-  if (cookie->locsymcount > 1)
+  if (loc_shndx != NULL)
     {
-      unsigned int shndx = elf_section_data (sec)->this_idx;
-      Elf_Internal_Sym *end_sym = cookie->locsyms + cookie->locsymcount;
-      Elf_Internal_Sym *sym;
+      unsigned int i;
 
-      for (sym = cookie->locsyms + 1; sym < end_sym; ++sym)
-       if (sym->st_info <= ELF_ST_INFO (STB_LOCAL, STT_OBJECT)
-           && sym->st_shndx == shndx)
-         {
-           bfd_signed_vma delta = offset_adjust (sym->st_value, sec);
+      for (i = 1; i < cookie->locsymcount; i++)
+       if (loc_shndx[i] == shndx)
+         break;
+      if (i >= cookie->locsymcount)
+       return NULL;
+    }
 
-           if (delta != 0)
-             {
-               adjusted = 1;
-               sym->st_value += delta;
-             }
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  Elf_Internal_Sym *locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                                   cookie->locsymcount, 0,
+                                                   NULL, NULL, NULL);
+  if (locsyms == NULL)
+    return NULL;
+
+  bool adjusted = false;
+  Elf_Internal_Sym *sym;
+  Elf_Internal_Sym *end_sym = locsyms + cookie->locsymcount;
+  for (sym = locsyms + 1; sym < end_sym; ++sym)
+    if (sym->st_info <= ELF_ST_INFO (STB_LOCAL, STT_OBJECT)
+       && sym->st_shndx == shndx)
+      {
+       bfd_signed_vma delta = offset_adjust (sym->st_value, sec);
+
+       if (delta != 0)
+         {
+           adjusted = true;
+           sym->st_value += delta;
          }
-    }
-  return adjusted;
+      }
+  if (adjusted)
+    return locsyms;
+  free (locsyms);
+  return NULL;
 }
 
 /* This function is called for each input file before the .eh_frame
@@ -1610,10 +1625,14 @@ _bfd_elf_discard_section_eh_frame
   if (sec->size != sec->rawsize)
     changed = 1;
 
-  if (changed && adjust_eh_frame_local_symbols (sec, cookie))
+  if (changed)
     {
-      Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-      symtab_hdr->contents = (unsigned char *) cookie->locsyms;
+      Elf_Internal_Sym *locsyms = adjust_eh_frame_local_symbols (sec, cookie);
+      if (locsyms != NULL)
+       {
+         Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+         symtab_hdr->contents = (unsigned char *) locsyms;
+       }
     }
   return changed;
 }
index 26dab93c5383a3f7f287d2b23bb0eb0e27e1dd7b..b706cf4d438c36576cda47d0fdefadf448fca9b7 100644 (file)
@@ -6069,15 +6069,22 @@ ppc64_elf_gc_mark_hook (asection *sec,
   else
     {
       struct _opd_sec_data *opd;
-      Elf_Internal_Sym *sym = &cookie->locsyms[symndx];
 
-      rsec = bfd_section_from_elf_index (sec->owner, sym->st_shndx);
+      rsec = _bfd_get_local_sym_section (cookie, symndx);
       opd = get_opd_info (rsec);
       if (opd != NULL && opd->func_sec != NULL)
        {
          rsec->gc_mark = 1;
 
-         rsec = opd->func_sec[OPD_NDX (sym->st_value + cookie->rel->r_addend)];
+         struct ppc_link_hash_table *htab = ppc_hash_table (info);
+         Elf_Internal_Sym *sym
+           = bfd_sym_from_r_symndx (&htab->elf.sym_cache, cookie->abfd,
+                                    symndx);
+         if (sym)
+           {
+             bfd_vma addr = sym->st_value + cookie->rel->r_addend;
+             rsec = opd->func_sec[OPD_NDX (addr)];
+           }
        }
     }
 
index 5f8ba26453df479ba9bd162561fb2af8c1a019ea..989a7a0a6afec7bf8a6202313de61ba0b656044f 100644 (file)
@@ -122,11 +122,44 @@ get_ext_sym_hash_from_cookie (struct elf_reloc_cookie *cookie,
 {
   if (cookie == NULL)
     return NULL;
-  
-  return _bfd_elf_get_link_hash_entry (cookie->sym_hashes, symndx,
+
+  return _bfd_elf_get_link_hash_entry (elf_sym_hashes (cookie->abfd), symndx,
                                       cookie->extsymoff);
 }
 
+asection *
+_bfd_get_local_sym_section (struct elf_reloc_cookie *cookie,
+                           unsigned int symndx)
+{
+  if (symndx >= cookie->locsymcount)
+    return NULL;
+
+  bfd *abfd = cookie->abfd;
+  if (elf_loc_shndx (abfd) == NULL)
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      Elf_Internal_Sym *locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                                       cookie->locsymcount, 0,
+                                                       NULL, NULL, NULL);
+      if (locsyms == NULL)
+       return NULL;
+      unsigned int *loc_shndx
+       = bfd_alloc (abfd, cookie->locsymcount * sizeof (*loc_shndx));
+      if (loc_shndx == NULL)
+       return NULL;
+      elf_loc_shndx (abfd) = loc_shndx;
+      for (unsigned int i = 0; i < cookie->locsymcount; i++)
+       {
+         loc_shndx[i] = locsyms[i].st_shndx;
+         if (ELF_ST_BIND (locsyms[i].st_info) != STB_LOCAL)
+           loc_shndx[i] = SHN_BAD;
+       }
+      free (locsyms);
+    }
+
+  return bfd_section_from_elf_index (abfd, elf_loc_shndx (abfd)[symndx]);
+}
+
 asection *
 _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
                             unsigned long r_symndx)
@@ -144,8 +177,7 @@ _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
        return NULL;
     }
 
-  Elf_Internal_Sym *isym = &cookie->locsyms[r_symndx];
-  return bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
+  return _bfd_get_local_sym_section (cookie, r_symndx);
 }
 
 /* Define a symbol in a dynamic linkage section.  */
@@ -4810,8 +4842,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
        {
          /* We store a pointer to the hash table entry for each
             external symbol.  */
-         size_t amt = extsymcount * sizeof (struct elf_link_hash_entry *);
-         sym_hash = (struct elf_link_hash_entry **) bfd_zalloc (abfd, amt);
+         sym_hash = bfd_zalloc (abfd, extsymcount * sizeof (*sym_hash));
          if (sym_hash == NULL)
            goto error_free_sym;
          elf_sym_hashes (abfd) = sym_hash;
@@ -11347,7 +11378,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
   if ((input_bfd->flags & DYNAMIC) != 0)
     return true;
 
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  symtab_hdr = &elf_symtab_hdr (input_bfd);
   if (elf_bad_symtab (input_bfd))
     {
       locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
@@ -13801,9 +13832,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 /* Initialize COOKIE for input bfd ABFD.  */
 
 static bool
-init_reloc_cookie (struct elf_reloc_cookie *cookie,
-                  struct bfd_link_info *info, bfd *abfd,
-                  bool keep_memory)
+init_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
 {
   Elf_Internal_Shdr *symtab_hdr;
   const struct elf_backend_data *bed;
@@ -13812,9 +13841,7 @@ init_reloc_cookie (struct elf_reloc_cookie *cookie,
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
 
   cookie->abfd = abfd;
-  cookie->sym_hashes = elf_sym_hashes (abfd);
-  cookie->bad_symtab = elf_bad_symtab (abfd);
-  if (cookie->bad_symtab)
+  if (elf_bad_symtab (abfd))
     {
       cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
       cookie->extsymoff = 0;
@@ -13830,37 +13857,15 @@ init_reloc_cookie (struct elf_reloc_cookie *cookie,
   else
     cookie->r_sym_shift = 32;
 
-  cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
-  if (cookie->locsyms == NULL && cookie->locsymcount != 0)
-    {
-      cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-                                             cookie->locsymcount, 0,
-                                             NULL, NULL, NULL);
-      if (cookie->locsyms == NULL)
-       {
-         info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
-         return false;
-       }
-      if (keep_memory || _bfd_elf_link_keep_memory (info))
-       {
-         symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
-         info->cache_size += (cookie->locsymcount
-                              * sizeof (Elf_Internal_Sym));
-       }
-    }
   return true;
 }
 
 /* Free the memory allocated by init_reloc_cookie, if appropriate.  */
 
 static void
-fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+fini_reloc_cookie (struct elf_reloc_cookie *cookie ATTRIBUTE_UNUSED,
+                  bfd *abfd ATTRIBUTE_UNUSED)
 {
-  Elf_Internal_Shdr *symtab_hdr;
-
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  if (symtab_hdr->contents != (unsigned char *) cookie->locsyms)
-    free (cookie->locsyms);
 }
 
 /* Initialize the relocation information in COOKIE for input section SEC
@@ -13908,7 +13913,7 @@ init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
                               struct bfd_link_info *info,
                               asection *sec, bool keep_memory)
 {
-  if (!init_reloc_cookie (cookie, info, sec->owner, keep_memory))
+  if (!init_reloc_cookie (cookie, sec->owner))
     goto error1;
   if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec,
                               keep_memory))
@@ -13937,17 +13942,14 @@ fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
 /* Default gc_mark_hook.  */
 
 asection *
-_bfd_elf_gc_mark_hook (asection *sec,
+_bfd_elf_gc_mark_hook (asection *sec ATTRIBUTE_UNUSED,
                       struct bfd_link_info *info ATTRIBUTE_UNUSED,
                       struct elf_reloc_cookie *cookie,
                       struct elf_link_hash_entry *h,
                       unsigned int symndx)
 {
   if (h == NULL)
-    {
-      Elf_Internal_Sym *sym = &cookie->locsyms[symndx];
-      return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-    }
+    return _bfd_get_local_sym_section (cookie, symndx);
 
   switch (h->root.type)
     {
@@ -13983,9 +13985,7 @@ elf_gc_mark_debug_section (asection *sec ATTRIBUTE_UNUSED,
   else
     {
       /* Return the local debug definition section.  */
-      Elf_Internal_Sym *sym = &cookie->locsyms[symndx];
-      asection *isec = bfd_section_from_elf_index (sec->owner,
-                                                  sym->st_shndx);
+      asection *isec = _bfd_get_local_sym_section (cookie, symndx);
       if (isec != NULL && (isec->flags & SEC_DEBUGGING) != 0)
        return isec;
     }
@@ -14580,7 +14580,7 @@ bfd_elf_parse_eh_frame_entries (bfd *abfd ATTRIBUTE_UNUSED,
       if (sec == NULL || sec->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
        continue;
 
-      if (!init_reloc_cookie (&cookie, info, ibfd, false))
+      if (!init_reloc_cookie (&cookie, ibfd))
        return false;
 
       for (sec = ibfd->sections; sec; sec = sec->next)
@@ -15056,16 +15056,16 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
 {
   struct elf_reloc_cookie *rcookie = (struct elf_reloc_cookie *) cookie;
 
-  if (rcookie->bad_symtab)
+  if (elf_bad_symtab (rcookie->abfd))
     rcookie->rel = rcookie->rels;
 
   for (; rcookie->rel < rcookie->relend; rcookie->rel++)
     {
       unsigned long r_symndx;
 
-      if (! rcookie->bad_symtab)
-       if (rcookie->rel->r_offset > offset)
-         return false;
+      if (!elf_bad_symtab (rcookie->abfd)
+         && rcookie->rel->r_offset > offset)
+       return false;
       if (rcookie->rel->r_offset != offset)
        continue;
 
@@ -15095,12 +15095,7 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
          /* It's not a relocation against a global symbol,
             but it could be a relocation against a local
             symbol for a discarded section.  */
-         asection *isec;
-         Elf_Internal_Sym *isym;
-
-         /* Need to: get the symbol; get the section.  */
-         isym = &rcookie->locsyms[r_symndx];
-         isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx);
+         asection *isec = _bfd_get_local_sym_section (cookie, r_symndx);
          if (isec != NULL
              && (isec->kept_section != NULL
                  || discarded_section (isec)))
@@ -15276,7 +15271,7 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 
       if (bed->elf_backend_discard_info != NULL)
        {
-         if (!init_reloc_cookie (&cookie, info, abfd, false))
+         if (!init_reloc_cookie (&cookie, abfd))
            return -1;
 
          if ((*bed->elf_backend_discard_info) (abfd, &cookie, info))