@@ -4195,8 +4195,9 @@ loongarch_relax_tls_le (bfd *abfd, asection *sec,
/* Relax pcalau12i,addi.d => pcaddi. */
static bool
loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec,
- Elf_Internal_Rela *rel_hi, bfd_vma symval,
- struct bfd_link_info *info, bool *again)
+ Elf_Internal_Rela *rel_hi, bfd_vma symval,
+ struct bfd_link_info *info, bool *again,
+ bfd_vma max_alignment)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
Elf_Internal_Rela *rel_lo = rel_hi + 2;
@@ -4212,14 +4213,22 @@ loongarch_relax_pcala_addi (bfd *abfd, asection *sec, asection *sym_sec,
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? */
+ 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 -= info->maxpagesize;
+ pc -= max_alignment;
else if (symval < pc)
- pc += info->maxpagesize;
+ pc += max_alignment;
}
+ else
+ if (symval > pc)
+ pc -= max_alignment;
+ else if (symval < pc)
+ pc += max_alignment;
const uint32_t addi_d = 0x02c00000;
const uint32_t pcaddi = 0x18000000;
@@ -4359,8 +4368,9 @@ loongarch_relax_align (bfd *abfd, asection *sec,
/* Relax pcalau12i + addi.d of TLS LD/GD/DESC to pcaddi. */
static bool
loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec,
- Elf_Internal_Rela *rel_hi, bfd_vma symval,
- struct bfd_link_info *info, bool *again)
+ Elf_Internal_Rela *rel_hi, bfd_vma symval,
+ struct bfd_link_info *info, bool *again,
+ bfd_vma max_alignment)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
Elf_Internal_Rela *rel_lo = rel_hi + 2;
@@ -4379,11 +4389,18 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec,
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 -= info->maxpagesize;
+ pc -= max_alignment;
else if (symval < pc)
- pc += info->maxpagesize;
+ pc += max_alignment;
}
+ else
+ if (symval > pc)
+ pc -= max_alignment;
+ else if (symval < pc)
+ pc += max_alignment;
const uint32_t addi_d = 0x02c00000;
const uint32_t pcaddi = 0x18000000;
@@ -4435,6 +4452,21 @@ loongarch_relax_tls_ld_gd_desc (bfd *abfd, asection *sec, asection *sym_sec,
return true;
}
+/* Traverse all output sections and return the max alignment. */
+
+static bfd_vma
+loongarch_get_max_alignment (asection *sec)
+{
+ asection *o;
+ unsigned int max_alignment_power = 0;
+
+ for (o = sec->output_section->owner->sections; o != NULL; o = o->next)
+ if (o->alignment_power > max_alignment_power)
+ max_alignment_power = o->alignment_power;
+
+ return (bfd_vma) 1 << max_alignment_power;
+}
+
static bool
loongarch_elf_relax_section (bfd *abfd, asection *sec,
struct bfd_link_info *info,
@@ -4445,6 +4477,7 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
Elf_Internal_Rela *relocs;
*again = false;
+ bfd_vma max_alignment = 0;
if (bfd_link_relocatable (info)
|| sec->sec_flg0
@@ -4477,6 +4510,15 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
data->relocs = relocs;
+ /* Estimate the maximum alignment for all output sections once time
+ should be enough. */
+ max_alignment = htab->max_alignment;
+ if (max_alignment == (bfd_vma) -1)
+ {
+ max_alignment = loongarch_get_max_alignment (sec);
+ htab->max_alignment = max_alignment;
+ }
+
for (unsigned int i = 0; i < sec->reloc_count; i++)
{
char symtype;
@@ -4613,6 +4655,7 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
if (1 == info->relax_pass)
loongarch_relax_align (abfd, sec, sym_sec, info, rel, symval);
break;
+
case R_LARCH_DELETE:
if (1 == info->relax_pass)
{
@@ -4620,6 +4663,7 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
}
break;
+
case R_LARCH_TLS_LE_HI20_R:
case R_LARCH_TLS_LE_LO12_R:
case R_LARCH_TLS_LE_ADD_R:
@@ -4630,34 +4674,25 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
case R_LARCH_PCALA_HI20:
if (0 == info->relax_pass && (i + 4) <= sec->reloc_count)
loongarch_relax_pcala_addi (abfd, sec, sym_sec, rel, symval,
- info, again);
+ info, again, max_alignment);
break;
+
case R_LARCH_GOT_PC_HI20:
if (local_got && 0 == info->relax_pass
&& (i + 4) <= sec->reloc_count)
{
if (loongarch_relax_pcala_ld (abfd, sec, rel))
loongarch_relax_pcala_addi (abfd, sec, sym_sec, rel, symval,
- info, again);
+ info, again, max_alignment);
}
break;
case R_LARCH_TLS_LD_PC_HI20:
- if (0 == info->relax_pass && (i + 4) <= sec->reloc_count)
- loongarch_relax_tls_ld_gd_desc (abfd, sec, sym_sec, rel, symval,
- info, again);
- break;
-
case R_LARCH_TLS_GD_PC_HI20:
- if (0 == info->relax_pass && (i + 4) <= sec->reloc_count)
- loongarch_relax_tls_ld_gd_desc (abfd, sec, sym_sec, rel, symval,
- info, again);
- break;
-
case R_LARCH_TLS_DESC_PC_HI20:
if (0 == info->relax_pass && (i + 4) <= sec->reloc_count)
loongarch_relax_tls_ld_gd_desc (abfd, sec, sym_sec, rel, symval,
- info, again);
+ info, again, max_alignment);
break;
default:
new file mode 100644
@@ -0,0 +1,25 @@
+# relocation overflow because .align
+.text
+ addi.d $t0, $t1, 0
+ addi.d $t0, $t1, 0
+ # Add one NOP instruction
+ .align 3
+ addi.d $t0, $t1, 0
+
+.section ".t.a", "ax"
+ addi.d $t0, $t1, 0
+ # In one try:
+ # first pass, la.local can be relaxed (0x120200010 - 0x120000014 = 0x1ffffc)
+ # second pass, the NOP addend by .align be deleted and pc decrease 4,
+ # but .L1 not decrease because section alignment.
+ # (0x120200010 - 0x120000010 = 0x200000)
+ la.local $t0, .L1
+ .fill 0x1ffff0
+
+.section ".t.b", "ax"
+.L1:
+ addi.d $t0, $t1, 0
+ # To make section address not change when first .align 3 delete NOP
+ .align 4
+ addi.d $t0, $t1, 0
+
@@ -164,14 +164,14 @@ if [istarget loongarch64-*-*] {
]
}
- if [file exist "tmpdir/relax-so"] {
- set objdump_output [run_host_cmd "objdump" "-d tmpdir/relax-so"]
- if { [ regexp ".*pcaddi.*" $objdump_output] } {
- pass "loongarch relax .so"
- } {
- fail "loongarch relax .so"
- }
+ if [file exist "tmpdir/relax-so"] {
+ set objdump_output [run_host_cmd "objdump" "-d tmpdir/relax-so"]
+ if { [ regexp ".*pcaddi.*" $objdump_output] } {
+ pass "loongarch relax .so"
+ } {
+ fail "loongarch relax .so"
}
+ }
# If symbol in data segment, offset need to sub segment align to prevent
# overflow.
@@ -201,6 +201,18 @@ if [istarget loongarch64-*-*] {
]
}
+ run_ld_link_tests \
+ [list \
+ [list \
+ "loongarch relax section align overflow" \
+ "-e0 -Ttext 0x120000000" "" \
+ "" \
+ {relax-section-align-overflow.s} \
+ {} \
+ "relax-section-align-overflow" \
+ ] \
+ ]
+
if [file exist "tmpdir/relax-tls-le"] {
set objdump_output1 [run_host_cmd "objdump" "-d tmpdir/relax-tls-le"]
if { [ regexp ".addi.*st.*" $objdump_output1] } {