]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[Morello] Identify branch source and target using mapping symbols
authorSiddhesh Poyarekar <siddesh.poyarekar@arm.com>
Fri, 11 Sep 2020 03:48:05 +0000 (09:18 +0530)
committerJohn Baldwin <jhb@FreeBSD.org>
Thu, 1 Sep 2022 22:53:20 +0000 (15:53 -0700)
Initialise section map data so that it can be used to identify C64
branch targets.  This is a reliable way (as long as mapping symbols
are correctly placed!) to identify branch source and target types in
cases where the target type is not STT_FUNC.  STT_FUNC targets can
already be identified using the LSB in the symbol table.

Use cases where the branch relocations are used (such as
elfNN_aarch64_size_stubs) have been adjusted to use the symbol cache
instead of reading the symbol table all over again.  In addition to
being faster, it will also allow identification of the relocation
targets using the st_target_internal set earlier by check_relocs.

bfd/ChangeLog:

2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>

* elf-bfd.h (bfd_elf_section_data): New member
is_target_section_data.
* elfnn-aarch64.c (_aarch64_elf_section_data): New member
sorted.
(elf_aarch64_section_data_get, c64_value_p): New functions.
(elf_aarch64_compare_mapping): Move function up in the file.
(elf_aarch64_obj_tdata): New member secmaps_initialised.
(bfd_elfNN_aarch64_init_maps, bfd_elfNN_aarch64_set_options):
Use it.
(elfNN_aarch64_size_stubs): Use symbol cache.
(elfNN_aarch64_check_relocs): Call
bfd_elfNN_aarch64_init_maps.  Mark C64 symbols in relocations
in the symbol cache.

gas/ChangeLog:

2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>

* config/tc-aarch64.c (FIXUP_F_C64): New macro.
(output_inst, parse_operands): Use it.
(aarch64_force_relocation, aarch64_fix_adjustable): Defer
relocation of branches with different source and targets to
the linker.
* config/tc-aarch64.h (aarch64_fix): New member c64.

bfd/ChangeLog
bfd/elf-bfd.h
bfd/elfnn-aarch64.c
gas/ChangeLog
gas/config/tc-aarch64.c
gas/config/tc-aarch64.h

index 5a42f1c44bb8d1334e3abcfd4ab6651e4d1b4c45..0a5aa97aed24b48c444e701eca7a5a1787eec2ae 100644 (file)
@@ -1,3 +1,19 @@
+2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>
+
+       * elf-bfd.h (bfd_elf_section_data): New member
+       is_target_section_data.
+       * elfnn-aarch64.c (_aarch64_elf_section_data): New member
+       sorted.
+       (elf_aarch64_section_data_get, c64_value_p): New functions.
+       (elf_aarch64_compare_mapping): Move function up in the file.
+       (elf_aarch64_obj_tdata): New member secmaps_initialised.
+       (bfd_elfNN_aarch64_init_maps, bfd_elfNN_aarch64_set_options):
+       Use it.
+       (elfNN_aarch64_size_stubs): Use symbol cache.
+       (elfNN_aarch64_check_relocs): Call
+       bfd_elfNN_aarch64_init_maps.  Mark C64 symbols in relocations
+       in the symbol cache.
+
 2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>
 
        * elfnn-aarch64.c (section_list,
index c7c0a793b1595b2cd3f9bbec5b5b51a960dc9c89..d05ca3d9a847e2e5e63d00b7f85a6ef3f73c387f 100644 (file)
@@ -1803,6 +1803,11 @@ struct bfd_elf_section_data
      of secondary reloc sections, making lookup easier and faster.  */
   bool has_secondary_relocs;
 
+  /* TRUE if this section data is allocated by the target and FALSE otherwise.
+     At the moment this flag is only useful for targets that set it and not for
+     the generic code.  */
+  bool is_target_section_data;
+
   /* A pointer used for various section optimizations.  */
   void *sec_info;
 };
index d67a2dac37f91c8ab519be9a3077668e4efc98fd..70d19a27468d778333fe13efdb3dce003161da67 100644 (file)
@@ -2489,12 +2489,86 @@ typedef struct _aarch64_elf_section_data
   unsigned int mapcount;
   unsigned int mapsize;
   elf_aarch64_section_map *map;
+  bool sorted;
 }
 _aarch64_elf_section_data;
 
 #define elf_aarch64_section_data(sec) \
   ((_aarch64_elf_section_data *) elf_section_data (sec))
 
+/* Used to order a list of mapping symbols by address.  */
+
+static int
+elf_aarch64_compare_mapping (const void *a, const void *b)
+{
+  const elf_aarch64_section_map *amap = (const elf_aarch64_section_map *) a;
+  const elf_aarch64_section_map *bmap = (const elf_aarch64_section_map *) b;
+
+  if (amap->vma > bmap->vma)
+    return 1;
+  else if (amap->vma < bmap->vma)
+    return -1;
+  else if (amap->type > bmap->type)
+    /* Ensure results do not depend on the host qsort for objects with
+       multiple mapping symbols at the same address by sorting on type
+       after vma.  */
+    return 1;
+  else if (amap->type < bmap->type)
+    return -1;
+  else
+    return 0;
+}
+
+static _aarch64_elf_section_data *
+elf_aarch64_section_data_get (asection *sec)
+{
+  _aarch64_elf_section_data *sec_data = elf_aarch64_section_data(sec);
+
+  /* A section that does not have aarch64 section data, so it does not have any
+     map information.  Assume A64.  */
+  if (sec_data == NULL || !sec_data->elf.is_target_section_data)
+    return NULL;
+
+  if (sec_data->sorted)
+    goto done;
+
+  qsort (sec_data->map, sec_data->mapcount, sizeof (elf_aarch64_section_map),
+        elf_aarch64_compare_mapping);
+
+  sec_data->sorted = true;
+
+done:
+  return sec_data;
+}
+
+/* Returns TRUE if the label with st_value as VALUE is within a C64 code
+   section or not.  */
+
+static bool
+c64_value_p (asection *section, unsigned int value)
+{
+  struct _aarch64_elf_section_data *sec_data =
+    elf_aarch64_section_data_get (section);
+
+  if (sec_data == NULL)
+    return false;
+
+  unsigned int span;
+
+  for (span = 0; span < sec_data->mapcount; span++)
+    {
+      unsigned int span_start = sec_data->map[span].vma;
+      unsigned int span_end = ((span == sec_data->mapcount - 1)
+                              ? sec_data->map[0].vma + section->size
+                              : sec_data->map[span + 1].vma);
+      char span_type = sec_data->map[span].type;
+
+      if (span_start <= value && value < span_end && span_type == 'c')
+       return true;
+    }
+  return false;
+}
+
 /* The size of the thread control block which is defined to be two pointers.  */
 #define TCB_SIZE       (ARCH_SIZE/8)*2
 
@@ -2535,6 +2609,10 @@ struct elf_aarch64_obj_tdata
 
   /* PLT type based on security.  */
   aarch64_plt_type plt_type;
+
+  /* Flag to check if section maps have been initialised for all sections in
+     this object.  */
+  bool secmaps_initialised;
 };
 
 #define elf_aarch64_tdata(bfd)                         \
@@ -3853,29 +3931,6 @@ aarch64_erratum_sequence (uint32_t insn_1, uint32_t insn_2)
   return false;
 }
 
-/* Used to order a list of mapping symbols by address.  */
-
-static int
-elf_aarch64_compare_mapping (const void *a, const void *b)
-{
-  const elf_aarch64_section_map *amap = (const elf_aarch64_section_map *) a;
-  const elf_aarch64_section_map *bmap = (const elf_aarch64_section_map *) b;
-
-  if (amap->vma > bmap->vma)
-    return 1;
-  else if (amap->vma < bmap->vma)
-    return -1;
-  else if (amap->type > bmap->type)
-    /* Ensure results do not depend on the host qsort for objects with
-       multiple mapping symbols at the same address by sorting on type
-       after vma.  */
-    return 1;
-  else if (amap->type < bmap->type)
-    return -1;
-  else
-    return 0;
-}
-
 
 static char *
 _bfd_aarch64_erratum_835769_stub_name (unsigned num_fixes)
@@ -4355,7 +4410,6 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
        {
          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)
@@ -4438,24 +4492,15 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                  if (r_indx < symtab_hdr->sh_info)
                    {
                      /* It's a local symbol.  */
-                     Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
+                     Elf_Internal_Sym *sym =
+                       bfd_sym_from_r_symndx (&htab->root.sym_cache,
+                                              input_bfd, r_indx);
+                     if (sym == NULL)
+                       goto error_ret_free_internal;
 
-                     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;
-                       }
+                     Elf_Internal_Shdr *hdr =
+                       elf_elfsections (input_bfd)[sym->st_shndx];
 
-                     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
@@ -4676,12 +4721,28 @@ elfNN_aarch64_build_stubs (struct bfd_link_info *info)
 /* Add an entry to the code/data map for section SEC.  */
 
 static void
-elfNN_aarch64_section_map_add (asection *sec, char type, bfd_vma vma)
+elfNN_aarch64_section_map_add (bfd *abfd, asection *sec, char type,
+                              bfd_vma vma)
 {
   struct _aarch64_elf_section_data *sec_data =
     elf_aarch64_section_data (sec);
   unsigned int newidx;
 
+  /* The aarch64 section hook was not called for this section.  */
+  if (!sec_data->elf.is_target_section_data)
+    {
+      struct _aarch64_elf_section_data *newdata =
+       bfd_zalloc (abfd, sizeof (*newdata));
+
+      if (newdata == NULL)
+       return;
+
+      newdata->elf = sec_data->elf;
+      newdata->elf.is_target_section_data = true;
+      free (sec_data);
+      sec->used_by_bfd = sec_data = newdata;
+    }
+
   if (sec_data->map == NULL)
     {
       sec_data->map = bfd_malloc (sizeof (elf_aarch64_section_map));
@@ -4718,6 +4779,9 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
   if (!is_aarch64_elf (abfd))
     return;
 
+  if (elf_aarch64_tdata (abfd)->secmaps_initialised)
+    return;
+
   if ((abfd->flags & DYNAMIC) != 0)
    return;
 
@@ -4747,9 +4811,10 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
 
          if (bfd_is_aarch64_special_symbol_name
              (name, BFD_AARCH64_SPECIAL_SYM_TYPE_MAP))
-           elfNN_aarch64_section_map_add (sec, name[1], isym->st_value);
+           elfNN_aarch64_section_map_add (abfd, sec, name[1], isym->st_value);
        }
     }
+  elf_aarch64_tdata (abfd)->secmaps_initialised = true;
 }
 
 static void
@@ -4832,6 +4897,7 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
     }
   elf_aarch64_tdata (output_bfd)->plt_type = bp_info.plt_type;
   setup_plt_values (link_info, bp_info.plt_type);
+  elf_aarch64_tdata (output_bfd)->secmaps_initialised = false;
 }
 
 static bfd_vma
@@ -5450,6 +5516,7 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
   bfd_vma orig_value = value;
   bool resolved_to_zero;
   bool abs_symbol_p;
+  Elf_Internal_Sym *isym = NULL;
 
   globals = elf_aarch64_hash_table (info);
 
@@ -5471,6 +5538,10 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
                  : bfd_is_und_section (sym_sec));
   abs_symbol_p = h != NULL && bfd_is_abs_symbol (&h->root);
 
+  if (sym)
+    isym = bfd_sym_from_r_symndx (&globals->root.sym_cache, input_bfd,
+                                 r_symndx);
+
 
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
      it here if it is defined in a non-shared object.  */
@@ -5897,8 +5968,8 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
                                                   signed_addend,
                                                   weak_undef_p);
 
-      if (bfd_r_type == BFD_RELOC_AARCH64_ADR_LO21_PCREL && sym != NULL
-         && sym->st_target_internal & ST_BRANCH_TO_C64)
+      if (bfd_r_type == BFD_RELOC_AARCH64_ADR_LO21_PCREL && isym != NULL
+         && isym->st_target_internal & ST_BRANCH_TO_C64)
        value |= 1;
       break;
 
@@ -5938,8 +6009,8 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
       value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
                                                   place, value,
                                                   signed_addend, weak_undef_p);
-      if (bfd_r_type == BFD_RELOC_AARCH64_ADD_LO12 && sym != NULL
-         && sym->st_target_internal & ST_BRANCH_TO_C64)
+      if (bfd_r_type == BFD_RELOC_AARCH64_ADD_LO12 && isym != NULL
+         && isym->st_target_internal & ST_BRANCH_TO_C64)
        value |= 1;
 
       break;
@@ -7601,6 +7672,8 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
 
+  bfd_elfNN_aarch64_init_maps (abfd);
+
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -7984,10 +8057,35 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
        case BFD_RELOC_AARCH64_CALL26:
        case BFD_RELOC_AARCH64_JUMP26:
-         /* If this is a local symbol then we resolve it
-            directly without creating a PLT entry.  */
          if (h == NULL)
-           continue;
+           {
+             isym = bfd_sym_from_r_symndx (&htab->root.sym_cache, abfd,
+                                           r_symndx);
+             if (isym == NULL)
+               return false;
+
+             asection *s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+             if (s == NULL)
+               s = sec;
+
+             if (c64_value_p (s, isym->st_value))
+               isym->st_target_internal |= ST_BRANCH_TO_C64;
+
+             /* If this is a local symbol then we resolve it
+                directly without creating a PLT entry.  */
+             continue;
+           }
+
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           {
+             asection *sym_sec = h->root.u.def.section;
+             bfd_vma sym_value = h->root.u.def.value;
+
+             if (sym_sec != NULL && c64_value_p (sym_sec, sym_value))
+               h->target_internal |= ST_BRANCH_TO_C64;
+           }
 
          h->needs_plt = 1;
          if (h->plt.refcount <= 0)
@@ -8443,6 +8541,7 @@ elfNN_aarch64_new_section_hook (bfd *abfd, asection *sec)
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
        return false;
+      sdata->elf.is_target_section_data = true;
       sec->used_by_bfd = sdata;
     }
 
index 9605a7431d7cf9d085ff960f974a9a3fa63bf0d1..85986964f85cbb828e2df550fe234e4d11411dbe 100644 (file)
@@ -1,3 +1,12 @@
+2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>
+
+       * config/tc-aarch64.c (FIXUP_F_C64): New macro.
+       (output_inst, parse_operands): Use it.
+       (aarch64_force_relocation, aarch64_fix_adjustable): Defer
+       relocation of branches with different source and targets to
+       the linker.
+       * config/tc-aarch64.h (aarch64_fix): New member c64.
+
 2020-10-20  Siddhesh Poyarekar  <siddesh.poyarekar@arm.com>
 
        * config/tc-aarch64.c: Include cpu-aarch64.h.
index 81b1f0a537225360a416a35ce43802584fa58743..8806731af9e720412cf36dfa0af6f63646ddfef5 100644 (file)
@@ -132,6 +132,7 @@ struct vector_type_el
 };
 
 #define FIXUP_F_HAS_EXPLICIT_SHIFT     0x00000001
+#define FIXUP_F_C64                    0x00000002
 
 struct reloc
 {
@@ -3191,6 +3192,26 @@ aarch64_force_reloc (unsigned int type)
       /* Always leave these relocations for the linker.  */
       return 1;
 
+    case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
+    case BFD_RELOC_AARCH64_BRANCH19:
+    case BFD_RELOC_AARCH64_TSTBR14:
+    case BFD_RELOC_AARCH64_CALL26:
+    case BFD_RELOC_AARCH64_JUMP26:
+      gas_assert (fixp->fx_addsy != NULL);
+
+      /* A jump/call destination will get adjusted to section+offset only
+        if both caller and callee are of the same type.  */
+      if (symbol_section_p (fixp->fx_addsy))
+       break;
+
+      if ((fixp->tc_fix_data.c64
+          && !AARCH64_IS_C64 (fixp->fx_addsy))
+         || (!fixp->tc_fix_data.c64
+             && AARCH64_IS_C64 (fixp->fx_addsy)))
+       return 1;
+
+      break;
+
     default:
       return -1;
     }
@@ -5814,6 +5835,8 @@ output_inst (struct aarch64_inst *new_inst)
          fixp->tc_fix_data.opnd = inst.reloc.opnd;
          fixp->fx_addnumber = inst.reloc.flags;
        }
+      if (inst.reloc.flags & FIXUP_F_C64)
+       fixp->tc_fix_data.c64 = true;
     }
 
   dwarf2_emit_insn (INSN_SIZE);
@@ -6998,6 +7021,9 @@ parse_operands (char *str, const aarch64_opcode *opcode)
            }
          else
            {
+             bool c64 = AARCH64_CPU_HAS_FEATURE (cpu_variant,
+                                                 AARCH64_FEATURE_C64);
+
              info->imm.value = 0;
              if (inst.reloc.type == BFD_RELOC_UNUSED)
                switch (opcode->iclass)
@@ -7047,6 +7073,8 @@ parse_operands (char *str, const aarch64_opcode *opcode)
                    gas_assert (0);
                    abort ();
                  }
+             if (c64)
+               inst.reloc.flags = FIXUP_F_C64;
              inst.reloc.pc_rel = 1;
            }
          break;
@@ -9507,16 +9535,21 @@ aarch64_fix_adjustable (struct fix *fixP)
 {
   switch (fixP->fx_r_type)
     {
-      /* A64 <-> C64 transitions are handled by the static linker, so keep
-        symbol information intact to allow the linker to do something useful
-        with it.  */
+      /* We need to retain symbol information when jumping between A64 and C64
+        states or between two C64 functions.  In the C64 -> C64 situation it's
+        really only a corner case that breaks when symbols get replaced with
+        section symbols; this is when the jump distance is longer than what a
+        branch instruction can handle and we want to branch through a stub.
+        In such a case, the linker needs to know the symbol types of the
+        source and the destination and section symbols are an unreliable
+        source of this information.  */
     case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
     case BFD_RELOC_AARCH64_ADD_LO12:
     case BFD_RELOC_AARCH64_BRANCH19:
     case BFD_RELOC_AARCH64_TSTBR14:
     case BFD_RELOC_AARCH64_JUMP26:
     case BFD_RELOC_AARCH64_CALL26:
-      if (AARCH64_IS_C64 (fixP->fx_addsy))
+      if (fixP->tc_fix_data.c64 || AARCH64_IS_C64 (fixP->fx_addsy))
        return false;
       break;
     default:
index 67deb2133e20c5c32af09c24257f32ee22221dbb..0b411f0076da6fd8afbf35b8e664f1372467b45f 100644 (file)
@@ -57,6 +57,7 @@ struct aarch64_fix
 {
   struct aarch64_inst *inst;
   enum aarch64_opnd opnd;
+  bool c64;
 };
 
 #if defined OBJ_ELF