bfd: aarch64: Refactor stub sizing code
authorSzabolcs Nagy <szabolcs.nagy@arm.com>
Mon, 30 Jan 2023 12:57:54 +0000 (12:57 +0000)
committerSzabolcs Nagy <szabolcs.nagy@arm.com>
Thu, 23 Mar 2023 12:49:32 +0000 (12:49 +0000)
elfNN_aarch64_size_stubs has grown big, so factor out the call stub
related code before adding new logic there.

bfd/elfnn-aarch64.c

index bf8f913a66dee9507b98fa3fda45280f8ea91656..f858d10c59694a73a15b3c8aaaeb6f2c3fe70840 100644 (file)
@@ -4265,12 +4265,285 @@ _bfd_aarch64_erratum_843419_scan (bfd *input_bfd, asection *section,
 }
 
 
-/* Determine and set the size of the stub section for a final link.
+/* Add stub entries for calls.
 
    The basic idea here is to examine all the relocations looking for
    PC-relative calls to a target that is unreachable with a "bl"
    instruction.  */
 
+static bool
+_bfd_aarch64_add_call_stub_entries (bool *stub_changed, bfd *output_bfd,
+                                   struct bfd_link_info *info)
+{
+  struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info);
+  bfd *input_bfd;
+
+  for (input_bfd = info->input_bfds; input_bfd != NULL;
+       input_bfd = input_bfd->link.next)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+      asection *section;
+      Elf_Internal_Sym *local_syms = NULL;
+
+      if (!is_aarch64_elf (input_bfd)
+         || (input_bfd->flags & BFD_LINKER_CREATED) != 0)
+       continue;
+
+      /* We'll need the symbol table in a second.  */
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (symtab_hdr->sh_info == 0)
+       continue;
+
+      /* Walk over each section attached to the input bfd.  */
+      for (section = input_bfd->sections;
+          section != NULL; section = section->next)
+       {
+         Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+         /* If there aren't any relocs, then there's nothing more to do.  */
+         if ((section->flags & SEC_RELOC) == 0
+             || section->reloc_count == 0
+             || (section->flags & SEC_CODE) == 0)
+           continue;
+
+         /* If this section is a link-once section that will be
+            discarded, then don't create any stubs.  */
+         if (section->output_section == NULL
+             || section->output_section->owner != output_bfd)
+           continue;
+
+         /* Get the relocs.  */
+         internal_relocs
+           = _bfd_elf_link_read_relocs (input_bfd, section, NULL,
+                                        NULL, info->keep_memory);
+         if (internal_relocs == NULL)
+           goto error_ret_free_local;
+
+         /* Now examine each relocation.  */
+         irela = internal_relocs;
+         irelaend = irela + section->reloc_count;
+         for (; irela < irelaend; irela++)
+           {
+             unsigned int r_type, r_indx;
+             enum elf_aarch64_stub_type stub_type;
+             struct elf_aarch64_stub_hash_entry *stub_entry;
+             asection *sym_sec;
+             bfd_vma sym_value;
+             bfd_vma destination;
+             struct elf_aarch64_link_hash_entry *hash;
+             const char *sym_name;
+             char *stub_name;
+             const asection *id_sec;
+             unsigned char st_type;
+             bfd_size_type len;
+
+             r_type = ELFNN_R_TYPE (irela->r_info);
+             r_indx = ELFNN_R_SYM (irela->r_info);
+
+             if (r_type >= (unsigned int) R_AARCH64_end)
+               {
+                 bfd_set_error (bfd_error_bad_value);
+               error_ret_free_internal:
+                 if (elf_section_data (section)->relocs == NULL)
+                   free (internal_relocs);
+                 goto error_ret_free_local;
+               }
+
+             /* Only look for stubs on unconditional branch and
+                branch and link instructions.  */
+             if (r_type != (unsigned int) AARCH64_R (CALL26)
+                 && r_type != (unsigned int) AARCH64_R (JUMP26))
+               continue;
+
+             /* Now determine the call target, its name, value,
+                section.  */
+             sym_sec = NULL;
+             sym_value = 0;
+             destination = 0;
+             hash = NULL;
+             sym_name = NULL;
+             if (r_indx < symtab_hdr->sh_info)
+               {
+                 /* It's a local symbol.  */
+                 Elf_Internal_Sym *sym;
+                 Elf_Internal_Shdr *hdr;
+
+                 if (local_syms == NULL)
+                   {
+                     local_syms
+                       = (Elf_Internal_Sym *) symtab_hdr->contents;
+                     if (local_syms == NULL)
+                       local_syms
+                         = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
+                                                 symtab_hdr->sh_info, 0,
+                                                 NULL, NULL, NULL);
+                     if (local_syms == NULL)
+                       goto error_ret_free_internal;
+                   }
+
+                 sym = local_syms + r_indx;
+                 hdr = elf_elfsections (input_bfd)[sym->st_shndx];
+                 sym_sec = hdr->bfd_section;
+                 if (!sym_sec)
+                   /* This is an undefined symbol.  It can never
+                      be resolved.  */
+                   continue;
+
+                 if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
+                   sym_value = sym->st_value;
+                 destination = (sym_value + irela->r_addend
+                                + sym_sec->output_offset
+                                + sym_sec->output_section->vma);
+                 st_type = ELF_ST_TYPE (sym->st_info);
+                 sym_name
+                   = bfd_elf_string_from_elf_section (input_bfd,
+                                                      symtab_hdr->sh_link,
+                                                      sym->st_name);
+               }
+             else
+               {
+                 int e_indx;
+
+                 e_indx = r_indx - symtab_hdr->sh_info;
+                 hash = ((struct elf_aarch64_link_hash_entry *)
+                         elf_sym_hashes (input_bfd)[e_indx]);
+
+                 while (hash->root.root.type == bfd_link_hash_indirect
+                        || hash->root.root.type == bfd_link_hash_warning)
+                   hash = ((struct elf_aarch64_link_hash_entry *)
+                           hash->root.root.u.i.link);
+
+                 if (hash->root.root.type == bfd_link_hash_defined
+                     || hash->root.root.type == bfd_link_hash_defweak)
+                   {
+                     struct elf_aarch64_link_hash_table *globals =
+                       elf_aarch64_hash_table (info);
+                     sym_sec = hash->root.root.u.def.section;
+                     sym_value = hash->root.root.u.def.value;
+                     /* For a destination in a shared library,
+                        use the PLT stub as target address to
+                        decide whether a branch stub is
+                        needed.  */
+                     if (globals->root.splt != NULL && hash != NULL
+                         && hash->root.plt.offset != (bfd_vma) - 1)
+                       {
+                         sym_sec = globals->root.splt;
+                         sym_value = hash->root.plt.offset;
+                         if (sym_sec->output_section != NULL)
+                           destination = (sym_value
+                                          + sym_sec->output_offset
+                                          + sym_sec->output_section->vma);
+                       }
+                     else if (sym_sec->output_section != NULL)
+                       destination = (sym_value + irela->r_addend
+                                      + sym_sec->output_offset
+                                      + sym_sec->output_section->vma);
+                   }
+                 else if (hash->root.root.type == bfd_link_hash_undefined
+                          || (hash->root.root.type
+                              == bfd_link_hash_undefweak))
+                   {
+                     /* For a shared library, use the PLT stub as
+                        target address to decide whether a long
+                        branch stub is needed.
+                        For absolute code, they cannot be handled.  */
+                     struct elf_aarch64_link_hash_table *globals =
+                       elf_aarch64_hash_table (info);
+
+                     if (globals->root.splt != NULL && hash != NULL
+                         && hash->root.plt.offset != (bfd_vma) - 1)
+                       {
+                         sym_sec = globals->root.splt;
+                         sym_value = hash->root.plt.offset;
+                         if (sym_sec->output_section != NULL)
+                           destination = (sym_value
+                                          + sym_sec->output_offset
+                                          + sym_sec->output_section->vma);
+                       }
+                     else
+                       continue;
+                   }
+                 else
+                   {
+                     bfd_set_error (bfd_error_bad_value);
+                     goto error_ret_free_internal;
+                   }
+                 st_type = ELF_ST_TYPE (hash->root.type);
+                 sym_name = hash->root.root.root.string;
+               }
+
+             /* Determine what (if any) linker stub is needed.  */
+             stub_type = aarch64_type_of_stub (section, irela, sym_sec,
+                                               st_type, destination);
+             if (stub_type == aarch64_stub_none)
+               continue;
+
+             /* Support for grouping stub sections.  */
+             id_sec = htab->stub_group[section->id].link_sec;
+
+             /* Get the name of this stub.  */
+             stub_name = elfNN_aarch64_stub_name (id_sec, sym_sec, hash,
+                                                  irela);
+             if (!stub_name)
+               goto error_ret_free_internal;
+
+             stub_entry =
+               aarch64_stub_hash_lookup (&htab->stub_hash_table,
+                                         stub_name, false, false);
+             if (stub_entry != NULL)
+               {
+                 /* The proper stub has already been created.  */
+                 free (stub_name);
+
+                 /* Always update this stub's target since it may have
+                    changed after layout.  */
+                 stub_entry->target_value = sym_value + irela->r_addend;
+                 continue;
+               }
+
+             stub_entry = _bfd_aarch64_add_stub_entry_in_group
+               (stub_name, section, htab);
+             if (stub_entry == NULL)
+               {
+                 free (stub_name);
+                 goto error_ret_free_internal;
+               }
+
+             stub_entry->target_value = sym_value + irela->r_addend;
+             stub_entry->target_section = sym_sec;
+             stub_entry->stub_type = stub_type;
+             stub_entry->h = hash;
+             stub_entry->st_type = st_type;
+
+             if (sym_name == NULL)
+               sym_name = "unnamed";
+             len = sizeof (STUB_ENTRY_NAME) + strlen (sym_name);
+             stub_entry->output_name = bfd_alloc (htab->stub_bfd, len);
+             if (stub_entry->output_name == NULL)
+               {
+                 free (stub_name);
+                 goto error_ret_free_internal;
+               }
+
+             snprintf (stub_entry->output_name, len, STUB_ENTRY_NAME,
+                       sym_name);
+
+             *stub_changed = true;
+           }
+
+         /* We're done with the internal relocs, free them.  */
+         if (elf_section_data (section)->relocs == NULL)
+           free (internal_relocs);
+       }
+    }
+  return true;
+ error_ret_free_local:
+  return false;
+}
+
+
+/* Determine and set the size of the stub section for a final link.  */
+
 bool
 elfNN_aarch64_size_stubs (bfd *output_bfd,
                          bfd *stub_bfd,
@@ -4282,7 +4555,6 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
 {
   bfd_size_type stub_group_size;
   bool stubs_always_before_branch;
-  bool stub_changed = false;
   struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info);
   unsigned int num_erratum_835769_fixes = 0;
 
@@ -4357,285 +4629,19 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
       (*htab->layout_sections_again) ();
     }
 
-  while (1)
+  for (;;)
     {
-      bfd *input_bfd;
-
-      for (input_bfd = info->input_bfds;
-          input_bfd != NULL; input_bfd = input_bfd->link.next)
-       {
-         Elf_Internal_Shdr *symtab_hdr;
-         asection *section;
-         Elf_Internal_Sym *local_syms = NULL;
-
-         if (!is_aarch64_elf (input_bfd)
-             || (input_bfd->flags & BFD_LINKER_CREATED) != 0)
-           continue;
-
-         /* We'll need the symbol table in a second.  */
-         symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-         if (symtab_hdr->sh_info == 0)
-           continue;
-
-         /* Walk over each section attached to the input bfd.  */
-         for (section = input_bfd->sections;
-              section != NULL; section = section->next)
-           {
-             Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
-
-             /* If there aren't any relocs, then there's nothing more
-                to do.  */
-             if ((section->flags & SEC_RELOC) == 0
-                 || section->reloc_count == 0
-                 || (section->flags & SEC_CODE) == 0)
-               continue;
-
-             /* If this section is a link-once section that will be
-                discarded, then don't create any stubs.  */
-             if (section->output_section == NULL
-                 || section->output_section->owner != output_bfd)
-               continue;
-
-             /* Get the relocs.  */
-             internal_relocs
-               = _bfd_elf_link_read_relocs (input_bfd, section, NULL,
-                                            NULL, info->keep_memory);
-             if (internal_relocs == NULL)
-               goto error_ret_free_local;
-
-             /* Now examine each relocation.  */
-             irela = internal_relocs;
-             irelaend = irela + section->reloc_count;
-             for (; irela < irelaend; irela++)
-               {
-                 unsigned int r_type, r_indx;
-                 enum elf_aarch64_stub_type stub_type;
-                 struct elf_aarch64_stub_hash_entry *stub_entry;
-                 asection *sym_sec;
-                 bfd_vma sym_value;
-                 bfd_vma destination;
-                 struct elf_aarch64_link_hash_entry *hash;
-                 const char *sym_name;
-                 char *stub_name;
-                 const asection *id_sec;
-                 unsigned char st_type;
-                 bfd_size_type len;
-
-                 r_type = ELFNN_R_TYPE (irela->r_info);
-                 r_indx = ELFNN_R_SYM (irela->r_info);
-
-                 if (r_type >= (unsigned int) R_AARCH64_end)
-                   {
-                     bfd_set_error (bfd_error_bad_value);
-                   error_ret_free_internal:
-                     if (elf_section_data (section)->relocs == NULL)
-                       free (internal_relocs);
-                     goto error_ret_free_local;
-                   }
-
-                 /* Only look for stubs on unconditional branch and
-                    branch and link instructions.  */
-                 if (r_type != (unsigned int) AARCH64_R (CALL26)
-                     && r_type != (unsigned int) AARCH64_R (JUMP26))
-                   continue;
-
-                 /* Now determine the call target, its name, value,
-                    section.  */
-                 sym_sec = NULL;
-                 sym_value = 0;
-                 destination = 0;
-                 hash = NULL;
-                 sym_name = NULL;
-                 if (r_indx < symtab_hdr->sh_info)
-                   {
-                     /* It's a local symbol.  */
-                     Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
-
-                     if (local_syms == NULL)
-                       {
-                         local_syms
-                           = (Elf_Internal_Sym *) symtab_hdr->contents;
-                         if (local_syms == NULL)
-                           local_syms
-                             = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
-                                                     symtab_hdr->sh_info, 0,
-                                                     NULL, NULL, NULL);
-                         if (local_syms == NULL)
-                           goto error_ret_free_internal;
-                       }
-
-                     sym = local_syms + r_indx;
-                     hdr = elf_elfsections (input_bfd)[sym->st_shndx];
-                     sym_sec = hdr->bfd_section;
-                     if (!sym_sec)
-                       /* This is an undefined symbol.  It can never
-                          be resolved.  */
-                       continue;
-
-                     if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
-                       sym_value = sym->st_value;
-                     destination = (sym_value + irela->r_addend
-                                    + sym_sec->output_offset
-                                    + sym_sec->output_section->vma);
-                     st_type = ELF_ST_TYPE (sym->st_info);
-                     sym_name
-                       = bfd_elf_string_from_elf_section (input_bfd,
-                                                          symtab_hdr->sh_link,
-                                                          sym->st_name);
-                   }
-                 else
-                   {
-                     int e_indx;
+      bool stub_changed = false;
 
-                     e_indx = r_indx - symtab_hdr->sh_info;
-                     hash = ((struct elf_aarch64_link_hash_entry *)
-                             elf_sym_hashes (input_bfd)[e_indx]);
-
-                     while (hash->root.root.type == bfd_link_hash_indirect
-                            || hash->root.root.type == bfd_link_hash_warning)
-                       hash = ((struct elf_aarch64_link_hash_entry *)
-                               hash->root.root.u.i.link);
-
-                     if (hash->root.root.type == bfd_link_hash_defined
-                         || hash->root.root.type == bfd_link_hash_defweak)
-                       {
-                         struct elf_aarch64_link_hash_table *globals =
-                           elf_aarch64_hash_table (info);
-                         sym_sec = hash->root.root.u.def.section;
-                         sym_value = hash->root.root.u.def.value;
-                         /* For a destination in a shared library,
-                            use the PLT stub as target address to
-                            decide whether a branch stub is
-                            needed.  */
-                         if (globals->root.splt != NULL && hash != NULL
-                             && hash->root.plt.offset != (bfd_vma) - 1)
-                           {
-                             sym_sec = globals->root.splt;
-                             sym_value = hash->root.plt.offset;
-                             if (sym_sec->output_section != NULL)
-                               destination = (sym_value
-                                              + sym_sec->output_offset
-                                              +
-                                              sym_sec->output_section->vma);
-                           }
-                         else if (sym_sec->output_section != NULL)
-                           destination = (sym_value + irela->r_addend
-                                          + sym_sec->output_offset
-                                          + sym_sec->output_section->vma);
-                       }
-                     else if (hash->root.root.type == bfd_link_hash_undefined
-                              || (hash->root.root.type
-                                  == bfd_link_hash_undefweak))
-                       {
-                         /* For a shared library, use the PLT stub as
-                            target address to decide whether a long
-                            branch stub is needed.
-                            For absolute code, they cannot be handled.  */
-                         struct elf_aarch64_link_hash_table *globals =
-                           elf_aarch64_hash_table (info);
-
-                         if (globals->root.splt != NULL && hash != NULL
-                             && hash->root.plt.offset != (bfd_vma) - 1)
-                           {
-                             sym_sec = globals->root.splt;
-                             sym_value = hash->root.plt.offset;
-                             if (sym_sec->output_section != NULL)
-                               destination = (sym_value
-                                              + sym_sec->output_offset
-                                              +
-                                              sym_sec->output_section->vma);
-                           }
-                         else
-                           continue;
-                       }
-                     else
-                       {
-                         bfd_set_error (bfd_error_bad_value);
-                         goto error_ret_free_internal;
-                       }
-                     st_type = ELF_ST_TYPE (hash->root.type);
-                     sym_name = hash->root.root.root.string;
-                   }
-
-                 /* Determine what (if any) linker stub is needed.  */
-                 stub_type = aarch64_type_of_stub (section, irela, sym_sec,
-                                                   st_type, destination);
-                 if (stub_type == aarch64_stub_none)
-                   continue;
-
-                 /* Support for grouping stub sections.  */
-                 id_sec = htab->stub_group[section->id].link_sec;
-
-                 /* Get the name of this stub.  */
-                 stub_name = elfNN_aarch64_stub_name (id_sec, sym_sec, hash,
-                                                      irela);
-                 if (!stub_name)
-                   goto error_ret_free_internal;
-
-                 stub_entry =
-                   aarch64_stub_hash_lookup (&htab->stub_hash_table,
-                                             stub_name, false, false);
-                 if (stub_entry != NULL)
-                   {
-                     /* The proper stub has already been created.  */
-                     free (stub_name);
-                     /* Always update this stub's target since it may have
-                        changed after layout.  */
-                     stub_entry->target_value = sym_value + irela->r_addend;
-                     continue;
-                   }
-
-                 stub_entry = _bfd_aarch64_add_stub_entry_in_group
-                   (stub_name, section, htab);
-                 if (stub_entry == NULL)
-                   {
-                     free (stub_name);
-                     goto error_ret_free_internal;
-                   }
-
-                 stub_entry->target_value = sym_value + irela->r_addend;
-                 stub_entry->target_section = sym_sec;
-                 stub_entry->stub_type = stub_type;
-                 stub_entry->h = hash;
-                 stub_entry->st_type = st_type;
-
-                 if (sym_name == NULL)
-                   sym_name = "unnamed";
-                 len = sizeof (STUB_ENTRY_NAME) + strlen (sym_name);
-                 stub_entry->output_name = bfd_alloc (htab->stub_bfd, len);
-                 if (stub_entry->output_name == NULL)
-                   {
-                     free (stub_name);
-                     goto error_ret_free_internal;
-                   }
-
-                 snprintf (stub_entry->output_name, len, STUB_ENTRY_NAME,
-                           sym_name);
-
-                 stub_changed = true;
-               }
-
-             /* We're done with the internal relocs, free them.  */
-             if (elf_section_data (section)->relocs == NULL)
-               free (internal_relocs);
-           }
-       }
+      if (!_bfd_aarch64_add_call_stub_entries (&stub_changed, output_bfd, info))
+       return false;
 
       if (!stub_changed)
-       break;
+       return true;
 
       _bfd_aarch64_resize_stubs (htab);
-
-      /* Ask the linker to do its stuff.  */
       (*htab->layout_sections_again) ();
-      stub_changed = false;
     }
-
-  return true;
-
- error_ret_free_local:
-  return false;
 }
 
 /* Build all the stubs associated with the current output file.  The