]> 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)
committerLuis Machado <luis.machado@linaro.org>
Tue, 20 Oct 2020 18:01:55 +0000 (15:01 -0300)
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 1cf42c3c3f6fb37fef3b8500375f3df119305986..9be4da6fa19c10ccc7be6c39703c576ce5c9bab4 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 140a98594d7cd0cf533ad0430b31a2f05c1addac..173267ffa27d973761e06c5318c35ecc76ce7c4e 100644 (file)
@@ -1762,6 +1762,11 @@ struct bfd_elf_section_data
      of secondary reloc sections, making lookup easier and faster.  */
   bfd_boolean 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.  */
+  bfd_boolean is_target_section_data;
+
   /* A pointer used for various section optimizations.  */
   void *sec_info;
 };
index 0ecc59a719435865687f9da546eada2f21059a22..124424dfd833178a7e20777ba4423858515ecfe6 100644 (file)
@@ -2489,12 +2489,86 @@ typedef struct _aarch64_elf_section_data
   unsigned int mapcount;
   unsigned int mapsize;
   elf_aarch64_section_map *map;
+  bfd_boolean 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 bfd_boolean
+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.  */
+  bfd_boolean 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
@@ -5447,6 +5513,7 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
   bfd_vma orig_value = value;
   bfd_boolean resolved_to_zero;
   bfd_boolean abs_symbol_p;
+  Elf_Internal_Sym *isym = NULL;
 
   globals = elf_aarch64_hash_table (info);
 
@@ -5468,6 +5535,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.  */
@@ -5894,8 +5965,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;
 
@@ -5935,8 +6006,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;
@@ -7598,6 +7669,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++)
     {
@@ -7981,10 +8054,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)
@@ -8349,6 +8447,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 aed6c2d760576a2255dfe7bcf23f85e7c5c14d30..023beb72c09203312011d85f0f67fe7ca8492b9a 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 4616c39bf3c07dcff7ded48fb3eea760b0ab458a..a3722af98da31971e27ea356bc3cc913f0b50d42 100644 (file)
@@ -121,6 +121,7 @@ struct vector_type_el
 };
 
 #define FIXUP_F_HAS_EXPLICIT_SHIFT     0x00000001
+#define FIXUP_F_C64                    0x00000002
 
 struct reloc
 {
@@ -5144,6 +5145,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);
@@ -6317,6 +6320,9 @@ parse_operands (char *str, const aarch64_opcode *opcode)
            }
          else
            {
+             bfd_boolean c64 = AARCH64_CPU_HAS_FEATURE (cpu_variant,
+                                                        AARCH64_FEATURE_C64);
+
              info->imm.value = 0;
              if (inst.reloc.type == BFD_RELOC_UNUSED)
                switch (opcode->iclass)
@@ -6366,6 +6372,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;
@@ -8569,6 +8577,26 @@ aarch64_force_relocation (struct fix *fixp)
       /* 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:
       break;
     }
@@ -8709,16 +8737,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 18f409444dc9ae2dc751756b48902406142b1924..5b197d454518bd7e2416a5b0ae86b4d0b034896e 100644 (file)
@@ -57,6 +57,7 @@ struct aarch64_fix
 {
   struct aarch64_inst *inst;
   enum aarch64_opnd opnd;
+  bfd_boolean c64;
 };
 
 #if defined OBJ_ELF