diff options
author | mengqinggang <mengqinggang@loongson.cn> | 2024-05-22 14:27:08 +0800 |
---|---|---|
committer | liuzhensong <liuzhensong@loongson.cn> | 2024-05-27 17:41:04 +0800 |
commit | 6f779908fd6d7c85197ab13949a9dc5e6d4f1458 (patch) | |
tree | 00725fade1e0d17330a79b2b5a8aa10cae0edb14 /bfd | |
parent | 4250085217f2011335257fd3291cb50c939e9746 (diff) | |
download | gdb-6f779908fd6d7c85197ab13949a9dc5e6d4f1458.zip gdb-6f779908fd6d7c85197ab13949a9dc5e6d4f1458.tar.gz gdb-6f779908fd6d7c85197ab13949a9dc5e6d4f1458.tar.bz2 |
LoongArch: Fix relaxation overflow caused by ld -z separate-code
ld -z separate-code let .text and .rodata in two different but read only
segment. If the symbol and pc in two segment, the offset from pc to
symbol need to consider segment alignment.
Add a function 'loongarch_two_sections_in_same_segment' to determine
whether two sections are in the same segment.
Diffstat (limited to 'bfd')
-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: |