[PATCH] LoongArch: fix .got.plt dislocation in static PDE
mengqinggang
mengqinggang@loongson.cn
Mon Aug 17 03:23:19 GMT 2026
Hi Ruoyao,
Some comments below.
在 2026/8/2 12:07, Xi Ruoyao 写道:
> The code path handling R_LARCH_GOT{64*,{,_PC}_LO12} miscalculated the
> address of the .got.plt entry: it always counted the two .got.plt
> entries reserved for ld.so, but in static PDE those two entries do not
> exist (as in static PDE the PLT is solely for ifunc). Thus all
> .got.plt entries dislocated for two slots.
>
> Those affected relocations are practically always used together with
> the R_LARCH_GOT_{PC_,}HI20 relocation. In 2.46 and earlier releases,
> R_LARCH_GOT_{PC_,}HI20 set pointer_equality_needed, which caused the
> R_LARCH_GOT_* relocs resolved to a .got entry (targeting the PLT stub)
> instead of .got.plt in static PDE, thus the error was latent. But in
> the 2.47 release R_LARCH_GOT_{PC_,}HI20 no longer sets
> pointer_equality_needed so the error became exploitable.
>
> Fix the issue by factoring out the correct .got.plt entry addressing
> logic handling R_LARCH_GOT_{PC_,}HI20 into a subroutine and replace the
> incorrect logic for R_LARCH_GOT{64*,{,_PC}_LO12} with a call to the
> subroutine.
>
> Signed-off-by: Xi Ruoyao <xry111@xry111.site>
> ---
> bfd/elfnn-loongarch.c | 46 ++++++-------------
> ld/testsuite/ld-loongarch-elf/ifunc.exp | 8 ++++
> ld/testsuite/ld-loongarch-elf/static-ifunc.c | 27 +++++++++++
> .../ld-loongarch-elf/static-ifunc.out | 1 +
> 4 files changed, 51 insertions(+), 31 deletions(-)
> create mode 100644 ld/testsuite/ld-loongarch-elf/static-ifunc.c
> create mode 100644 ld/testsuite/ld-loongarch-elf/static-ifunc.out
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index 4bdc3f26285..658c5b93e08 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -3513,6 +3513,19 @@ loongarch_resolve_pcrel_lo_relocs (loongarch_pcrel_relocs *p)
> return true;
> }
>
> +static bfd_vma
> +ifunc_got_off (struct elf_link_hash_table *htab,
> + struct elf_link_hash_entry *h)
> +{
> + bfd_vma idx =
> + (h->plt.offset - (htab->splt ? PLT_HEADER_SIZE : 0)) / PLT_ENTRY_SIZE;
> +
> + return sec_addr (htab->sgotplt)
Would it make sense to use htab->igotplt when htab->splt is NULL?
Just like:
return sec_addr (htab->splt ? htab->sgotplt : htab->igotplt)
> + + (htab->splt ? GOTPLT_HEADER_SIZE : 0)
> + + (idx * GOT_ENTRY_SIZE)
> + - sec_addr (htab->sgot);
> +}
> +
> static int
> loongarch_elf_relocate_section (struct bfd_link_info *info,
> bfd *input_bfd, asection *input_section,
> @@ -4539,25 +4552,7 @@ loongarch_elf_relocate_section (struct bfd_link_info *info,
> /* Hidden symbol not has got entry,
> * only got.plt entry so it is (plt - got). */
> if (h->got.offset == MINUS_ONE && h->type == STT_GNU_IFUNC)
> - {
> - bfd_vma idx;
> - if (htab->elf.splt != NULL)
> - {
> - idx = (h->plt.offset - PLT_HEADER_SIZE)
> - / PLT_ENTRY_SIZE;
> - got_off = sec_addr (htab->elf.sgotplt)
> - + GOTPLT_HEADER_SIZE
> - + (idx * GOT_ENTRY_SIZE)
> - - sec_addr (htab->elf.sgot);
> - }
> - else
> - {
> - idx = h->plt.offset / PLT_ENTRY_SIZE;
> - got_off = sec_addr (htab->elf.sgotplt)
> - + (idx * GOT_ENTRY_SIZE)
> - - sec_addr (htab->elf.sgot);
> - }
> - }
> + got_off = ifunc_got_off (&htab->elf, h);
>
> if ((h->got.offset & 1) == 0)
> {
> @@ -4638,18 +4633,7 @@ loongarch_elf_relocate_section (struct bfd_link_info *info,
> got_off = local_got_offsets[r_symndx] & (~(bfd_vma)1);
>
> if (h && h->got.offset == MINUS_ONE && h->type == STT_GNU_IFUNC)
> - {
> - bfd_vma idx;
> - if (htab->elf.splt != NULL)
> - idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
> - else
> - idx = h->plt.offset / PLT_ENTRY_SIZE;
> -
> - got_off = sec_addr (htab->elf.sgotplt)
> - + GOTPLT_HEADER_SIZE
> - + (idx * GOT_ENTRY_SIZE)
> - - sec_addr (htab->elf.sgot);
> - }
> + got_off = ifunc_got_off (&htab->elf, h);
>
> relocation = got_off + sec_addr (got);
> }
> diff --git a/ld/testsuite/ld-loongarch-elf/ifunc.exp b/ld/testsuite/ld-loongarch-elf/ifunc.exp
> index da33eeb8061..f6c97ae2019 100644
> --- a/ld/testsuite/ld-loongarch-elf/ifunc.exp
> +++ b/ld/testsuite/ld-loongarch-elf/ifunc.exp
> @@ -30,5 +30,13 @@ if [istarget loongarch*-*-*] {
> "attr-ifunc-4" \
> "attr-ifunc-4.out" \
> ] \
> + [list \
> + "Run static-ifunc" \
> + "-static" \
> + "" \
> + {static-ifunc.c} \
> + "static-ifunc" \
> + "static-ifunc.out" \
> + ] \
> ]
> }
> diff --git a/ld/testsuite/ld-loongarch-elf/static-ifunc.c b/ld/testsuite/ld-loongarch-elf/static-ifunc.c
> new file mode 100644
> index 00000000000..c0b1788df7c
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/static-ifunc.c
> @@ -0,0 +1,27 @@
> +int
> +f1 ()
> +{
> + return 42;
> +}
> +int
> +f2 ()
> +{
> + return 47;
> +}
> +
> +void *
> +fx ()
> +{
> + return f1;
> +}
> +
> +[[gnu::ifunc ("fx")]] int f ();
> +
> +int
> +main ()
> +{
> + int (*p) () = f;
> + asm ("# prevent optimization" : "+r"(p));
> + __builtin_printf ("%d\n", p ());
> + return 0;
> +}
> diff --git a/ld/testsuite/ld-loongarch-elf/static-ifunc.out b/ld/testsuite/ld-loongarch-elf/static-ifunc.out
> new file mode 100644
> index 00000000000..d81cc0710eb
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/static-ifunc.out
> @@ -0,0 +1 @@
> +42
More information about the Binutils
mailing list