[PATCH] LoongArch: Fix symbol size after relaxation
mengqinggang
mengqinggang@loongson.cn
Wed Aug 6 03:59:19 GMT 2025
Hi Ruoyao:
Some modification suggestions may make the code better understood.
在 2025/8/4 11:34, Xi Ruoyao 写道:
> 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>
> ---
>
> For both the master branch and the 2.45 release branch.
>
> bfd/elfnn-loongarch.c | 105 +++++++++++-------
> .../ld-loongarch-elf/ld-loongarch-elf.exp | 2 +
> .../ld-loongarch-elf/relax-sym-size-1.d | 7 ++
> .../ld-loongarch-elf/relax-sym-size-1.s | 8 ++
> .../ld-loongarch-elf/relax-sym-size-2.d | 7 ++
> .../ld-loongarch-elf/relax-sym-size-2.s | 19 ++++
> 6 files changed, 105 insertions(+), 43 deletions(-)
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-sym-size-1.d
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-sym-size-1.s
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-sym-size-2.d
> create mode 100644 ld/testsuite/ld-loongarch-elf/relax-sym-size-2.s
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index 1ddea2f4f8c..062c795b6c4 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -4903,6 +4903,63 @@ 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)
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;
> + return;
> + }
> +
> + *size -= op->size;
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 +5058,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 +5106,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);
> }
> }
> }
> diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> index e23cdc87161..a33727f2efd 100644
> --- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> +++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> @@ -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"
> }
>
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.d b/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.d
> new file mode 100644
> index 00000000000..eec989f68be
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.d
> @@ -0,0 +1,7 @@
> +#source: relax-sym-size-1.s
> +#ld:
> +#readelf: -s
> +
> +#...
> + *[0-9]+: [0-9a-z]+ +8 .* _start
> +#...
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.s b/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.s
> new file mode 100644
> index 00000000000..c70ac7176bd
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-sym-size-1.s
> @@ -0,0 +1,8 @@
> +.p2align 2
> +bar:
> + nop
> +.globl _start
> +_start:
> + la.pcrel $a0, bar
> + ret
> +.size _start, . - _start
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.d b/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.d
> new file mode 100644
> index 00000000000..1388099d37f
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.d
> @@ -0,0 +1,7 @@
> +#source: relax-sym-size-2.s
> +#ld:
> +#readelf: -s
> +
> +#...
> + *[0-9]+: [0-9a-z]+ +64 .* _start
> +#...
> diff --git a/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.s b/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.s
> new file mode 100644
> index 00000000000..e02e16b7d26
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/relax-sym-size-2.s
> @@ -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
More information about the Binutils
mailing list