From: Siddhesh Poyarekar Date: Fri, 11 Sep 2020 03:48:05 +0000 (+0530) Subject: [Morello] Identify branch source and target using mapping symbols X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f0070c1e15648c37e46f52409a9482c1d98e651a;p=thirdparty%2Fbinutils-gdb.git [Morello] Identify branch source and target using mapping symbols 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 * 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 * 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. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 1cf42c3c3f6..9be4da6fa19 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,19 @@ +2020-10-20 Siddhesh Poyarekar + + * 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 * elfnn-aarch64.c (section_list, diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 140a98594d7..173267ffa27 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -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; }; diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c index 0ecc59a7194..124424dfd83 100644 --- a/bfd/elfnn-aarch64.c +++ b/bfd/elfnn-aarch64.c @@ -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; } diff --git a/gas/ChangeLog b/gas/ChangeLog index aed6c2d7605..023beb72c09 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,12 @@ +2020-10-20 Siddhesh Poyarekar + + * 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 * config/tc-aarch64.c: Include cpu-aarch64.h. diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index 4616c39bf3c..a3722af98da 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -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: diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h index 18f409444dc..5b197d45451 100644 --- a/gas/config/tc-aarch64.h +++ b/gas/config/tc-aarch64.h @@ -57,6 +57,7 @@ struct aarch64_fix { struct aarch64_inst *inst; enum aarch64_opnd opnd; + bfd_boolean c64; }; #if defined OBJ_ELF