diff options
Diffstat (limited to 'bfd/elfnn-loongarch.c')
-rw-r--r-- | bfd/elfnn-loongarch.c | 101 |
1 files changed, 62 insertions, 39 deletions
diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c index 4f1754e..e60bd4d 100644 --- a/bfd/elfnn-loongarch.c +++ b/bfd/elfnn-loongarch.c @@ -3949,6 +3949,12 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, info->callbacks->reloc_overflow (info, h ? &h->root : NULL, name, howto->name, rel->r_addend, input_bfd, input_section, rel->r_offset); + if (r_type == R_LARCH_PCREL20_S2 + || r_type == R_LARCH_TLS_LD_PCREL20_S2 + || r_type == R_LARCH_TLS_GD_PCREL20_S2 + || r_type == R_LARCH_TLS_DESC_PCREL20_S2) + _bfd_error_handler (_("recompile with 'gcc -mno-relax' or" + " 'as -mno-relax' or 'ld --no-relax'")); break; case bfd_reloc_outofrange: @@ -4313,6 +4319,30 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec, return true; } +/* Whether two sections in the same segment. */ +static bool +loongarch_two_sections_in_same_segment (bfd *abfd, asection *a, asection *b) +{ + struct elf_segment_map *m; + for (m = elf_seg_map (abfd); m != NULL; m = m->next) + { + int i; + int j = 0; + for (i = m->count - 1; i >= 0; i--) + { + if (m->sections[i] == a) + j++; + if (m->sections[i] == b) + j++; + } + if (1 == j) + return false; + if (2 == j) + return true; + } + return false; +} + /* Relax pcalau12i,addi.d => pcaddi. */ static bool loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec, @@ -4333,23 +4363,17 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec, sec->output_offset = sec->output_section->size; bfd_vma pc = sec_addr (sec) + rel_hi->r_offset; - /* If pc and symbol not in the same segment, add/sub segment alignment. - FIXME: if there are multiple readonly segments? How to determine if - two sections are in the same segment. */ - if (!(sym_sec->flags & SEC_READONLY)) - { - max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize - : max_alignment; - if (symval > pc) - pc -= max_alignment; - else if (symval < pc) - pc += max_alignment; - } - else - if (symval > pc) - pc -= max_alignment; - else if (symval < pc) - pc += max_alignment; + /* If pc and symbol not in the same segment, add/sub segment alignment. */ + if (!loongarch_two_sections_in_same_segment (info->output_bfd, + sec->output_section, + sym_sec->output_section)) + max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize + : max_alignment; + + if (symval > pc) + pc -= (max_alignment > 4 ? max_alignment : 0); + else if (symval < pc) + pc += (max_alignment > 4 ? max_alignment : 0); const uint32_t addi_d = 0x02c00000; const uint32_t pcaddi = 0x18000000; @@ -4388,7 +4412,7 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec, /* call36 f -> bl f tail36 $t0, f -> b f. */ static bool -loongarch_relax_call36 (bfd *abfd, asection *sec, +loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_sec, Elf_Internal_Rela *rel, bfd_vma symval, struct bfd_link_info *info, bool *again, bfd_vma max_alignment) @@ -4404,9 +4428,13 @@ loongarch_relax_call36 (bfd *abfd, asection *sec, sec->output_offset = sec->output_section->size; bfd_vma pc = sec_addr (sec) + rel->r_offset; - /* If pc and symbol not in the same segment, add/sub segment alignment. - FIXME: if there are multiple readonly segments? How to determine if - two sections are in the same segment. */ + /* If pc and symbol not in the same segment, add/sub segment alignment. */ + if (!loongarch_two_sections_in_same_segment (info->output_bfd, + sec->output_section, + sym_sec->output_section)) + max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize + : max_alignment; + if (symval > pc) pc -= (max_alignment > 4 ? max_alignment : 0); else if (symval < pc) @@ -4560,22 +4588,17 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec, sec->output_offset = sec->output_section->size; bfd_vma pc = sec_addr (sec) + rel_hi->r_offset; - /* If pc and symbol not in the same segment, add/sub segment alignment. - FIXME: if there are multiple readonly segments? */ - if (!(sym_sec->flags & SEC_READONLY)) - { - max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize - : max_alignment; - if (symval > pc) - pc -= max_alignment; - else if (symval < pc) - pc += max_alignment; - } - else - if (symval > pc) - pc -= max_alignment; - else if (symval < pc) - pc += max_alignment; + /* If pc and symbol not in the same segment, add/sub segment alignment. */ + if (!loongarch_two_sections_in_same_segment (info->output_bfd, + sec->output_section, + sym_sec->output_section)) + max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize + : max_alignment; + + if (symval > pc) + pc -= (max_alignment > 4 ? max_alignment : 0); + else if (symval < pc) + pc += (max_alignment > 4 ? max_alignment : 0); const uint32_t addi_d = 0x02c00000; const uint32_t pcaddi = 0x18000000; @@ -4859,8 +4882,8 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec, break; case R_LARCH_CALL36: if (0 == info->relax_pass && (i + 2) <= sec->reloc_count) - loongarch_relax_call36 (abfd, sec, rel, symval, info, again, - max_alignment); + loongarch_relax_call36 (abfd, sec, sym_sec, rel, symval, + info, again, max_alignment); break; case R_LARCH_TLS_LE_HI20_R: |