LoongArch: Fix R_LARCH_IRELATIVE insertion after elf_link_sort_relocs

loongarch_elf_finish_dynamic_symbol is called after elf_link_sort_relocs
if -z combreloc.  elf_link_sort_relocs redistributes the contents of
.rela.* sections those would be merged into .rela.dyn, so the slot for
R_LARCH_IRELATIVE may be out of relplt->contents now.

To make things worse, the boundary check

    dyn < dyn + relplt->size / sizeof (*dyn)

is obviously wrong ("x + 10 < x"? :), causing the issue undetected
during the linking process and the resulted executable suddenly crashes
at runtime.

The issue was found during an attempt to add static-pie support to the
toolchain.

Fix it by iterating through the inputs of .rela.dyn to find the slot.
This commit is contained in:
Xi Ruoyao
2022-09-20 14:09:30 +08:00
committed by liuzhensong
parent 6224a6c2ea
commit ae2e4d4035
4 changed files with 63 additions and 17 deletions

View File

@@ -3514,6 +3514,12 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
{
struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
asection *rela_dyn = bfd_get_section_by_name (output_bfd, ".rela.dyn");
struct bfd_link_order *lo = NULL;
Elf_Internal_Rela *slot = NULL, *last_slot = NULL;
if (rela_dyn)
lo = rela_dyn->map_head.link_order;
if (h->plt.offset != MINUS_ONE)
{
@@ -3523,6 +3529,7 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
uint32_t plt_entry[PLT_ENTRY_INSNS];
bfd_byte *loc;
Elf_Internal_Rela rela;
asection *rela_sec = NULL;
if (htab->elf.splt)
{
@@ -3575,31 +3582,31 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
&& (relplt == htab->elf.srelgot
|| relplt == htab->elf.irelplt))
{
{
rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
rela.r_addend = (h->root.u.def.value
rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
rela.r_addend = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
}
/* Find the space after dyn sort. */
/* Find the space after dyn sort. */
while (slot == last_slot || slot->r_offset != 0)
{
Elf_Internal_Rela *dyn = (Elf_Internal_Rela *)relplt->contents;
bool fill = false;
for (;dyn < dyn + relplt->size / sizeof (*dyn); dyn++)
if (slot != last_slot)
{
if (0 == dyn->r_offset)
{
bed->s->swap_reloca_out (output_bfd, &rela,
(bfd_byte *)dyn);
relplt->reloc_count++;
fill = true;
break;
}
slot++;
continue;
}
BFD_ASSERT (fill);
BFD_ASSERT (lo != NULL);
rela_sec = lo->u.indirect.section;
lo = lo->next;
slot = (Elf_Internal_Rela *)rela_sec->contents;
last_slot = (Elf_Internal_Rela *)(rela_sec->contents +
rela_sec->size);
}
bed->s->swap_reloca_out (output_bfd, &rela, (bfd_byte *)slot);
rela_sec->reloc_count++;
}
else
{

View File

@@ -31,6 +31,7 @@ if [istarget "loongarch64-*-*"] {
run_dump_test "macro_op"
run_dump_test "syscall"
run_dump_test "disas-jirl"
run_dump_test "local-ifunc-reloc"
}
if [istarget "loongarch32-*-*"] {

View File

@@ -0,0 +1,10 @@
#as:
#ld: -shared -z combreloc
#objdump: -R
.*: +file format .*
DYNAMIC RELOCATION RECORDS
OFFSET +TYPE +VALUE
[[:xdigit:]]+ R_LARCH_IRELATIVE +\*ABS\*\+0x[[:xdigit:]]+
[[:xdigit:]]+ R_LARCH_64 +test

View File

@@ -0,0 +1,28 @@
.text
.align 2
.local ifunc
.type ifunc, @gnu_indirect_function
.set ifunc, resolver
resolver:
la.local $a0, impl
jr $ra
impl:
li.w $a0, 42
jr $ra
.global test
.type test, @function
test:
move $s0, $ra
bl ifunc
xori $a0, $a0, 42
jr $s0
.data
.global ptr
.type ptr, @object
ptr:
.dword test