LoongArch: Fix symbol size after relaxation

There's a logic error in loongarch_relax_perform_deletes: when there's
not any delete operation of which the start address is strictly smaller
than the symbol address, splay_tree_predecessor() will return nullptr
and the symbol size will be unchanged even if some bytes of it are
removed.

Make the logic more complete to fix this issue.  Also factor out the
symbol size adjustment logic into a function to avoid code bloating.

Tested-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: Xi Ruoyao <xry111@xry111.site>
This commit is contained in:
Xi Ruoyao
2025-08-06 12:19:22 +08:00
committed by cailulu
parent 081bf6a3a1
commit 3eede6b04a
6 changed files with 102 additions and 43 deletions

View File

@@ -4903,6 +4903,60 @@ loongarch_relax_delete_or_nop (bfd *abfd,
bfd_put (32, abfd, LARCH_NOP, contents + addr);
}
/* If some bytes in a symbol is deleted, we need to adjust its size. */
static void
loongarch_relax_resize_symbol (bfd_size_type *size, bfd_vma orig_value,
splay_tree pdops)
{
splay_tree_key key = (splay_tree_key)orig_value;
bfd_vma orig_end = orig_value + *size;
splay_tree_node node = splay_tree_predecessor (pdops, key);
if (node)
{
bfd_vma addr = (bfd_vma)node->key;
struct pending_delete_op *op = (struct pending_delete_op *)node->value;
/* This shouldn't happen unless people write something really insane like
.reloc ., R_LARCH_ALIGN, 60
.rept 15
1: nop
.endr
.set x, 1b
.size x, . - 1b
But let's just try to make it "work" anyway. */
if (orig_value < addr + op->size)
{
bfd_size_type n_deleted = op->size - (orig_value - addr);
if (n_deleted >= *size)
{
*size = 0;
return;
}
*size -= n_deleted;
}
}
node = splay_tree_lookup (pdops, key);
if (!node)
node = splay_tree_successor (pdops, key);
for (; node; node = splay_tree_successor (pdops, node->key))
{
bfd_vma addr = (bfd_vma)node->key;
struct pending_delete_op *op = (struct pending_delete_op *)node->value;
if (addr >= orig_end)
return;
if (orig_end < addr + op->size)
*size -= orig_end - addr;
else
*size -= op->size;
}
}
static void
loongarch_relax_perform_deletes (bfd *abfd, asection *sec,
struct bfd_link_info *link_info)
@@ -5001,30 +5055,8 @@ loongarch_relax_perform_deletes (bfd *abfd, asection *sec,
sym->st_value
= loongarch_calc_relaxed_addr (link_info, orig_value);
/* If the symbol *spans* some deleted bytes, that is its *end* is in
the moved bytes but its *start* isn't, then we must adjust its
size.
This test needs to use the original value of st_value, otherwise
we might accidentally decrease size when deleting bytes right
before the symbol. */
bfd_vma sym_end = orig_value + sym->st_size;
if (sym_end <= toaddr)
{
splay_tree_node node = splay_tree_predecessor (
pdops, (splay_tree_key)orig_value);
for (; node; node = splay_tree_successor (pdops, node->key))
{
bfd_vma addr = (bfd_vma)node->key;
struct pending_delete_op *op
= (struct pending_delete_op *)node->value;
if (addr >= sym_end)
break;
if (orig_value <= addr && sym_end > addr)
sym->st_size -= op->size;
}
}
if (orig_value + sym->st_size <= toaddr)
loongarch_relax_resize_symbol (&sym->st_size, orig_value, pdops);
}
}
@@ -5071,29 +5103,13 @@ loongarch_relax_perform_deletes (bfd *abfd, asection *sec,
{
bfd_vma orig_value = sym_hash->root.u.def.value;
/* As above, adjust the value. */
/* As above, adjust the value and size. */
if (orig_value <= toaddr)
sym_hash->root.u.def.value
= loongarch_calc_relaxed_addr (link_info, orig_value);
/* As above, adjust the size if needed. */
bfd_vma sym_end = orig_value + sym_hash->size;
if (sym_end <= toaddr)
{
splay_tree_node node = splay_tree_predecessor (
pdops, (splay_tree_key)orig_value);
for (; node; node = splay_tree_successor (pdops, node->key))
{
bfd_vma addr = (bfd_vma)node->key;
struct pending_delete_op *op
= (struct pending_delete_op *)node->value;
if (addr >= sym_end)
break;
if (orig_value <= addr && sym_end > addr)
sym_hash->size -= op->size;
}
}
if (orig_value + sym_hash->size <= toaddr)
loongarch_relax_resize_symbol (&sym_hash->size, orig_value, pdops);
}
}
}

View File

@@ -48,6 +48,8 @@ if [istarget "loongarch64-*-*"] {
run_dump_test "relax-after-alignment"
run_dump_test "relax-medium-call"
run_dump_test "relax-medium-call-1"
run_dump_test "relax-sym-size-1"
run_dump_test "relax-sym-size-2"
run_dump_test "check_got_relax"
}

View File

@@ -0,0 +1,7 @@
#source: relax-sym-size-1.s
#ld:
#readelf: -s
#...
*[0-9]+: [0-9a-z]+ +8 .* _start
#...

View File

@@ -0,0 +1,8 @@
.p2align 2
bar:
nop
.globl _start
_start:
la.pcrel $a0, bar
ret
.size _start, . - _start

View File

@@ -0,0 +1,7 @@
#source: relax-sym-size-2.s
#ld:
#readelf: -s
#...
*[0-9]+: [0-9a-z]+ +64 .* _start
#...

View File

@@ -0,0 +1,19 @@
bar:
nop
.p2align 6
.reloc ., R_LARCH_ALIGN, 60
.rept 15
1: nop
.endr
la.pcrel $a0, bar
ret
.reloc ., R_LARCH_ALIGN, 60
.rept 15
2: nop
.endr
.globl _start
.set _start, 1b
.size _start, 2b - 1b