[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