[v2] RISC-V: Fix SIGSEGV of --static-pie binaries on riscv64 [BZ #33911]

Message ID 20260306151006.158727-1-daichengrong@iscas.ac.cn (mailing list archive)
State Changes Requested
Headers
Series [v2] RISC-V: Fix SIGSEGV of --static-pie binaries on riscv64 [BZ #33911] |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed
redhat-pt-bot/TryBot-32bit success Build for i686

Commit Message

daichengrong March 6, 2026, 3:10 p.m. UTC
  This patch fixes a SIGSEGV observed in --static-pie binaries on riscv64,
as reported in BZ #33911. The root cause is a recent memset optimization
that introduced incorrect behavior when resolving symbols for static PIE
executables.

Problem:
--------
In static PIE binaries, an early call to ifunc memset could use GOT entries
that were not yet properly initialized. This results in jumping to
invalid addresses, causing SIGSEGV.

Solution:
---------
This patch ensures that GOT entries for static PIE binaries are
correctly set up before ifunc memset calls occur.

Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
---
 sysdeps/riscv/cpu-features.c | 88 ++++++++++++++++++++++++++++++++++++
 sysdeps/riscv/dl-machine.h   |  4 +-
 sysdeps/riscv/libc-start.c   | 34 ++++++++++++++
 3 files changed, 124 insertions(+), 2 deletions(-)
 create mode 100644 sysdeps/riscv/cpu-features.c
 create mode 100644 sysdeps/riscv/libc-start.c
  

Comments

Adhemerval Zanella Netto March 6, 2026, 4:42 p.m. UTC | #1
On 06/03/26 12:10, daichengrong wrote:
> This patch fixes a SIGSEGV observed in --static-pie binaries on riscv64,
> as reported in BZ #33911. The root cause is a recent memset optimization
> that introduced incorrect behavior when resolving symbols for static PIE
> executables.
> 
> Problem:
> --------
> In static PIE binaries, an early call to ifunc memset could use GOT entries
> that were not yet properly initialized. This results in jumping to
> invalid addresses, causing SIGSEGV.

This is exactly what <dl-symbol-redir-ifunc.h> is for.

> 
> Solution:
> ---------
> This patch ensures that GOT entries for static PIE binaries are
> correctly set up before ifunc memset calls occur.

Sorry, but this kind of code duplication for arch-specific workarounds are not
acceptable. And this issue is not RISCV specific, other ABIs might implement 
memset through ifunc and being subject to the very issue if the compiler starts
to optimize more construction to libcalls.

One solution could to implement ELF_MACHINE_BEFORE_RTLD_RELOC for RISCV, or
restructure the generic elf/dl-reloc-static-pie.c to add another arch-specific
hook. Worse scenario, we just build the TU with -ftree-loop-distribute-patterns
to avoid any libcalls (and I would prefer to avoid it because this is a gcc
specific fix).

In any case, I think we can just fix it with the following patch. At least on
qemu I don't see any more SEGFAULTs with -Os.

diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index bdff2b5ee2..63ce609024 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -25,6 +25,7 @@

 #include <dl-machine.h>
 #include <dl-debug.h>
+#include <dl-symbol-redir-ifunc.h>

 #define RESOLVE_MAP(map, scope, sym, version, flags) map
 #include "dynamic-link.h"
diff --git a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
index 24b78711e3..69a0790838 100644
--- a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
@@ -21,6 +21,7 @@

 #ifndef SHARED
 asm ("memset = __memset_generic");
+asm ("memcpy = __memcpy_generic");
 #endif

 #endif
diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
index a865090a53..a033ea9569 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
@@ -11,4 +11,5 @@ sysdep_routines += \
 CFLAGS-memcpy_noalignment.c += -mno-strict-align
 # Called during static initialization
 CFLAGS-memset-generic.c += $(no-stack-protector)
+CFLAGS-memcpy-generic.c += $(no-stack-protector)
 endif

> 
> Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
> ---
>  sysdeps/riscv/cpu-features.c | 88 ++++++++++++++++++++++++++++++++++++
>  sysdeps/riscv/dl-machine.h   |  4 +-
>  sysdeps/riscv/libc-start.c   | 34 ++++++++++++++
>  3 files changed, 124 insertions(+), 2 deletions(-)
>  create mode 100644 sysdeps/riscv/cpu-features.c
>  create mode 100644 sysdeps/riscv/libc-start.c
> 
> diff --git a/sysdeps/riscv/cpu-features.c b/sysdeps/riscv/cpu-features.c
> new file mode 100644
> index 0000000000..56f694c31f
> --- /dev/null
> +++ b/sysdeps/riscv/cpu-features.c
> @@ -0,0 +1,88 @@
> +/* Initialize CPU feature data.
> +   This file is part of the GNU C Library.
> +   Copyright (C) 2026 Free Software Foundation, Inc.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +# pragma GCC visibility push(hidden)
> +#include <assert.h>
> +#include <unistd.h>
> +#include <ldsodefs.h>
> +
> +#include <dl-machine.h>
> +#include <dl-debug.h>
> +
> +#define RESOLVE_MAP(map, scope, sym, version, flags) map
> +#include "elf/dynamic-link.h"
> +#include "elf/get-dynamic-info.h"
> +
> +/* Relocate static executable with PIE for local ifunc symbol.  */
> +static void
> +init_cpu_features (void)
> +{
> +  ElfW(Addr) file_p_vaddr = 0;
> +  const ElfW(Phdr) *ph, *phdr = GL(dl_phdr);
> +  size_t phnum = GL(dl_phnum);
> +
> +  struct link_map *map = _dl_get_dl_main_map ();
> +
> +  for (ph = phdr; ph < &phdr[phnum]; ++ph)
> +    switch (ph->p_type)
> +      {
> +      case PT_LOAD:
> +	/* Skip the empty PT_LOAD segment at offset 0.  */
> +	if (ph->p_filesz != 0 && ph->p_offset == 0)
> +	  file_p_vaddr = ph->p_vaddr;
> +	break;
> +      case PT_DYNAMIC:
> +	map->l_ld_readonly = (ph->p_flags & PF_W) == 0;
> +	break;
> +      default:
> +	break;
> +      }
> +
> +  /* Figure out the run-time load address of static PIE.  */
> +  ElfW(Addr) l_addr = elf_machine_load_address ();
> +  map->l_addr = l_addr - file_p_vaddr;
> +
> +  map->l_ld = ((void *) l_addr + elf_machine_dynamic ());
> +
> +  elf_get_dynamic_info (map, false, true);
> +
> +  if ((map)->l_info[DT_PLTGOT] != NULL && (map)->l_info[DT_PLTGOT]->d_un.d_ptr != 0)
> +  {
> +    ElfW(Rela) *reloc = (ElfW(Rela) *)(map->l_info[DT_JMPREL]->d_un.d_val + map->l_addr);
> +    ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
> +
> +    ElfW(Addr) * start = (ElfW(Addr) *)(gotplt + 2);
> +    size_t rela_size = map->l_info[DT_PLTRELSZ]->d_un.d_val;
> +    size_t num_plt_entries = rela_size / 8;
> +
> +    ElfW(Addr)  plt_addr  = (ElfW(Addr))((ElfW(Addr) *)(*start) + 4) + (ElfW(Addr))map->l_addr;
> +    for (int ranges_index = 0; ranges_index < num_plt_entries; ++ranges_index)
> +      {
> +        ElfW(Addr) r_info = reloc->r_info;
> +        if(ELFW (R_TYPE) (r_info) == R_RISCV_IRELATIVE)
> +        {
> +          ElfW(Addr) addr = (ElfW(Addr))map->l_addr + reloc->r_addend;
> +          *start = ((ElfW(Addr) (*) (uint64_t, void *, void *)) (addr))(0, NULL, NULL);
> +        }
> +
> +        start++;
> +        reloc ++;
> +        plt_addr += 16;
> +      }
> +  }
> +}
> diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
> index 8c7312ad98..dfb202157c 100644
> --- a/sysdeps/riscv/dl-machine.h
> +++ b/sysdeps/riscv/dl-machine.h
> @@ -1,5 +1,5 @@
>  /* Machine-dependent ELF dynamic relocation inline functions.  RISC-V version.
> -   Copyright (C) 2011-2026 Free Software Foundation, Inc.
> +   Copyright (C) 2011-2025 Free Software Foundation, Inc.
>     This file is part of the GNU C Library.
>  
>     The GNU C Library is free software; you can redistribute it and/or
> @@ -26,7 +26,7 @@
>  #include <sys/asm.h>
>  #include <dl-tls.h>
>  #include <dl-irel.h>
> -#include <dl-static-tls.h>
> +#include <elf/dl-static-tls.h>
>  #include <dl-machine-rel.h>
>  
>  #ifndef _RTLD_PROLOGUE
> diff --git a/sysdeps/riscv/libc-start.c b/sysdeps/riscv/libc-start.c
> new file mode 100644
> index 0000000000..5584ce165f
> --- /dev/null
> +++ b/sysdeps/riscv/libc-start.c
> @@ -0,0 +1,34 @@
> +/* Override csu/libc-start.c on riscv.
> +   Copyright (C) 2026 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +# if ENABLE_STATIC_PIE
> +#ifndef SHARED
> +/* Relocate local IFUNC symbols in static PIE for early self relocation. */
> +# if BUILD_PIE_DEFAULT
> +#  pragma GCC visibility push(hidden)
> +# endif
> +
> +# include <startup.h>
> +# include <ldsodefs.h>
> +# include <cpu-features.c>
> +
> +# define ARCH_INIT_CPU_FEATURES() init_cpu_features()
> +
> +#endif /* !SHARED */
> +#endif
> +#include <csu/libc-start.c>
  
daichengrong March 9, 2026, 12:33 p.m. UTC | #2
On 3/7/26 00:42, Adhemerval Zanella Netto wrote:

> 
> Sorry, but this kind of code duplication for arch-specific workarounds are not
> acceptable. 

I agree with this point. Using arch-specific code duplication as a workaround
is not a good long-term solution.

In the v3 RFC reference implementation, I first tried to adjust the code
structure to avoid giving the compiler the opportunity to generate a memset
libcall. The idea was to explore this from the perspective of compiler
optimization behavior, i.e. trying to prevent certain patterns from being
optimized into calls to more complex libc routines.

> And this issue is not RISCV specific, other ABIs might implement 
> memset through ifunc and being subject to the very issue if the compiler starts
> to optimize more construction to libcalls.

I agree with this point. This issue is not necessarily limited to RISCV. If the
compiler starts optimizing more patterns into libcalls, and those libcalls are
implemented through ifunc, similar problems could arise before relocation is
complete.

It is also difficult to predict how many such libcalls the compiler might
generate in the future. For this reason, my current approach leans toward
adjusting the code structure to avoid giving the compiler the opportunity to
introduce libcalls before relocation has finished.

> One solution could to implement ELF_MACHINE_BEFORE_RTLD_RELOC for RISCV, or
> restructure the generic elf/dl-reloc-static-pie.c to add another arch-specific
> hook. 

> Worse scenario, we just build the TU with -ftree-loop-distribute-patterns
> to avoid any libcalls (and I would prefer to avoid it because this is a gcc
> specific fix).

Regarding the option you mentioned about using
-ftree-loop-distribute-patterns to avoid libcalls,

I am not entirely sure whether disabling this optimization for this TU
might cause us to lose some optimizations that the compiler would
otherwise perform, or lead to other unintended effects on the generated
code.

> In any case, I think we can just fix it with the following patch. At least on
> qemu I don't see any more SEGFAULTs with -Os.

> +#include <dl-symbol-redir-ifunc.h>
> 
> +asm ("memcpy = __memcpy_generic");

Libcalls are generally intended as a generic and reusable implementation.
However, for code paths like ELF relocation, which run in a very early stage
of execution and follow a fairly structured pattern, relying on such calls
may not be ideal.

For example, in the RFC v3 reference implementation, a memcpy was used even
though the number of bytes to copy is known at compile time:

 ElfW(Addr) value = l_addr + reloc->r_addend;
 memcpy(reloc_addr, &value, sizeof value);

Here the copy size is fixed and small, but using memcpy introduces a
dependency on the runtime implementation, which may not be fully available
during this very early stage. The same consideration applies to memset:
using a generic memset call in this context is convenient in general, but
not particularly friendly for structured early-relocation code paths.

> +CFLAGS-memcpy-generic.c += $(no-stack-protector)
> endif
> 

Overall, the implementation in RFC v3 is mainly intended as a reference
experiment to explore the feasibility of this approach. I will continue
to evaluate more structural and general solutions, taking into account the
early-stage constraints of ELF relocation and the points you suggested.

I welcome any further discussion or suggestions on how to best handle this
in a robust and maintainable way.

[1] https://sourceware.org/pipermail/libc-alpha/2026-March/175716.html
  
Adhemerval Zanella Netto March 9, 2026, 1:18 p.m. UTC | #3
On 09/03/26 09:33, daichengrong wrote:
> 
> 
> On 3/7/26 00:42, Adhemerval Zanella Netto wrote:
> 
>>
>> Sorry, but this kind of code duplication for arch-specific workarounds are not
>> acceptable.
> 
> I agree with this point. Using arch-specific code duplication as a workaround
> is not a good long-term solution.
> 
> In the v3 RFC reference implementation, I first tried to adjust the code
> structure to avoid giving the compiler the opportunity to generate a memset
> libcall. The idea was to explore this from the perspective of compiler
> optimization behavior, i.e. trying to prevent certain patterns from being
> optimized into calls to more complex libc routines.
> 
>> And this issue is not RISCV specific, other ABIs might implement
>> memset through ifunc and being subject to the very issue if the compiler starts
>> to optimize more construction to libcalls.
> 
> I agree with this point. This issue is not necessarily limited to RISCV. If the
> compiler starts optimizing more patterns into libcalls, and those libcalls are
> implemented through ifunc, similar problems could arise before relocation is
> complete.
> 
> It is also difficult to predict how many such libcalls the compiler might
> generate in the future. For this reason, my current approach leans toward
> adjusting the code structure to avoid giving the compiler the opportunity to
> introduce libcalls before relocation has finished.
> 
>> One solution could to implement ELF_MACHINE_BEFORE_RTLD_RELOC for RISCV, or
>> restructure the generic elf/dl-reloc-static-pie.c to add another arch-specific
>> hook.
> 
>> Worse scenario, we just build the TU with -ftree-loop-distribute-patterns
>> to avoid any libcalls (and I would prefer to avoid it because this is a gcc
>> specific fix).
> 
> Regarding the option you mentioned about using
> -ftree-loop-distribute-patterns to avoid libcalls,
> 
> I am not entirely sure whether disabling this optimization for this TU
> might cause us to lose some optimizations that the compiler would
> otherwise perform, or lead to other unintended effects on the generated
> code.

Well, your patch does exactly this by tricking the compiler to no emit the
libcalls.  Using a compiler switch is way clear about the intentions and
future-proof wrt any compiler optimization.

But the -ftree-loop-distribute-patterns is a hard switch and we only need
at one specific place now:

 533 #ifndef DONT_USE_BOOTSTRAP_MAP
 534 # ifdef HAVE_BUILTIN_MEMSET
 535   __builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
 536 # else
 537   for (size_t cnt = 0;
 538        cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
 539        ++cnt)
 540     bootstrap_map.l_info[cnt] = 0;
 541 # endif
 542 #endif

before static/ld self-relocation.

> 
>> In any case, I think we can just fix it with the following patch. At least on
>> qemu I don't see any more SEGFAULTs with -Os.
> 
>> +#include <dl-symbol-redir-ifunc.h>
>>
>> +asm ("memcpy = __memcpy_generic");
> 
> Libcalls are generally intended as a generic and reusable implementation.
> However, for code paths like ELF relocation, which run in a very early stage
> of execution and follow a fairly structured pattern, relying on such calls
> may not be ideal.
> 
> For example, in the RFC v3 reference implementation, a memcpy was used even
> though the number of bytes to copy is known at compile time:
> 
> ElfW(Addr) value = l_addr + reloc->r_addend;
> memcpy(reloc_addr, &value, sizeof value);
> 
> Here the copy size is fixed and small, but using memcpy introduces a
> dependency on the runtime implementation, which may not be fully available
> during this very early stage. The same consideration applies to memset:
> using a generic memset call in this context is convenient in general, but
> not particularly friendly for structured early-relocation code paths.

For -Os (as the bug reports state this is being generated) I would say that 
this exactly the intention: trade code size for performance.

Also, for known-sizes up to a certain value I would expect that compiler
to inline such calls since lowering to a libcall does not yield much
gain here (and I expect gains even for code size).

In any case, I do not think we should play clever here.

> 
>> +CFLAGS-memcpy-generic.c += $(no-stack-protector)
>> endif
>>
> 
> Overall, the implementation in RFC v3 is mainly intended as a reference
> experiment to explore the feasibility of this approach. I will continue
> to evaluate more structural and general solutions, taking into account the
> early-stage constraints of ELF relocation and the points you suggested.
> 
> I welcome any further discussion or suggestions on how to best handle this
> in a robust and maintainable way.
> 
> [1] https://sourceware.org/pipermail/libc-alpha/2026-March/175716.html <https://sourceware.org/pipermail/libc-alpha/2026-March/175716.html>

My proposal is what we do some code where compiler might issue libcalls and
it avoid fragile construction like you did to split the store [1] where 
without any compiler barrier or volatile use, compiler might just optimize 
this in the future.  To be fully correct you will need something like the
configure check we do for HAVE_BUILTIN_MEMSET.

The v3 also adds a complete unnecessary refactor to elf_machine_lazy_rel,
which would need to validate on *all* ABIs (last time I changed this to
remove nested function usage I broke some ABIs lie powerpc32). 

[1] https://sourceware.org/pipermail/libc-alpha/2026-March/175747.html
  
Jeffrey Law March 13, 2026, 7:10 p.m. UTC | #4
On 3/6/2026 9:42 AM, Adhemerval Zanella Netto wrote:
>
> On 06/03/26 12:10, daichengrong wrote:
>> This patch fixes a SIGSEGV observed in --static-pie binaries on riscv64,
>> as reported in BZ #33911. The root cause is a recent memset optimization
>> that introduced incorrect behavior when resolving symbols for static PIE
>> executables.
>>
>> Problem:
>> --------
>> In static PIE binaries, an early call to ifunc memset could use GOT entries
>> that were not yet properly initialized. This results in jumping to
>> invalid addresses, causing SIGSEGV.
> This is exactly what <dl-symbol-redir-ifunc.h> is for.
Right.


>
>> Solution:
>> ---------
>> This patch ensures that GOT entries for static PIE binaries are
>> correctly set up before ifunc memset calls occur.
> Sorry, but this kind of code duplication for arch-specific workarounds are not
> acceptable. And this issue is not RISCV specific, other ABIs might implement
> memset through ifunc and being subject to the very issue if the compiler starts
> to optimize more construction to libcalls.
>
> One solution could to implement ELF_MACHINE_BEFORE_RTLD_RELOC for RISCV, or
> restructure the generic elf/dl-reloc-static-pie.c to add another arch-specific
> hook. Worse scenario, we just build the TU with -ftree-loop-distribute-patterns
> to avoid any libcalls (and I would prefer to avoid it because this is a gcc
> specific fix).
>
> In any case, I think we can just fix it with the following patch. At least on
> qemu I don't see any more SEGFAULTs with -Os.
>
> diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
> index bdff2b5ee2..63ce609024 100644
> --- a/elf/dl-reloc-static-pie.c
> +++ b/elf/dl-reloc-static-pie.c
> @@ -25,6 +25,7 @@
>
>   #include <dl-machine.h>
>   #include <dl-debug.h>
> +#include <dl-symbol-redir-ifunc.h>
>
>   #define RESOLVE_MAP(map, scope, sym, version, flags) map
>   #include "dynamic-link.h"
> diff --git a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> index 24b78711e3..69a0790838 100644
> --- a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> @@ -21,6 +21,7 @@
>
>   #ifndef SHARED
>   asm ("memset = __memset_generic");
> +asm ("memcpy = __memcpy_generic");
>   #endif
I'm pretty sure I've got this in my local tree as well.

I'll spin your full patch up momentarily on my BPI for testing.   It 
generally looks correct to me.

jeff
  
Jeffrey Law March 21, 2026, 2 p.m. UTC | #5
On 3/6/2026 9:42 AM, Adhemerval Zanella Netto wrote:
>
> On 06/03/26 12:10, daichengrong wrote:
>> This patch fixes a SIGSEGV observed in --static-pie binaries on riscv64,
>> as reported in BZ #33911. The root cause is a recent memset optimization
>> that introduced incorrect behavior when resolving symbols for static PIE
>> executables.
>>
>> Problem:
>> --------
>> In static PIE binaries, an early call to ifunc memset could use GOT entries
>> that were not yet properly initialized. This results in jumping to
>> invalid addresses, causing SIGSEGV.
> This is exactly what <dl-symbol-redir-ifunc.h> is for.
>
>> Solution:
>> ---------
>> This patch ensures that GOT entries for static PIE binaries are
>> correctly set up before ifunc memset calls occur.
> Sorry, but this kind of code duplication for arch-specific workarounds are not
> acceptable. And this issue is not RISCV specific, other ABIs might implement
> memset through ifunc and being subject to the very issue if the compiler starts
> to optimize more construction to libcalls.
>
> One solution could to implement ELF_MACHINE_BEFORE_RTLD_RELOC for RISCV, or
> restructure the generic elf/dl-reloc-static-pie.c to add another arch-specific
> hook. Worse scenario, we just build the TU with -ftree-loop-distribute-patterns
> to avoid any libcalls (and I would prefer to avoid it because this is a gcc
> specific fix).
>
> In any case, I think we can just fix it with the following patch. At least on
> qemu I don't see any more SEGFAULTs with -Os.
>
> diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
> index bdff2b5ee2..63ce609024 100644
> --- a/elf/dl-reloc-static-pie.c
> +++ b/elf/dl-reloc-static-pie.c
> @@ -25,6 +25,7 @@
>
>   #include <dl-machine.h>
>   #include <dl-debug.h>
> +#include <dl-symbol-redir-ifunc.h>
>
>   #define RESOLVE_MAP(map, scope, sym, version, flags) map
>   #include "dynamic-link.h"
> diff --git a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> index 24b78711e3..69a0790838 100644
> --- a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
> @@ -21,6 +21,7 @@
>
>   #ifndef SHARED
>   asm ("memset = __memset_generic");
> +asm ("memcpy = __memcpy_generic");
>   #endif
>
>   #endif
> diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> index a865090a53..a033ea9569 100644
> --- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> +++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
> @@ -11,4 +11,5 @@ sysdep_routines += \
>   CFLAGS-memcpy_noalignment.c += -mno-strict-align
>   # Called during static initialization
>   CFLAGS-memset-generic.c += $(no-stack-protector)
> +CFLAGS-memcpy-generic.c += $(no-stack-protector)
>   endif
So I've tested this on a K1 system here without triggering any 
regressions.  Given the key implementation details match what we're 
running here *and* would be precisely what we'd need to include when we 
light up memcpy with RVV, I'm going to commit your patch in a few minutes.


Jeff
  
Jeffrey Law March 21, 2026, 2:09 p.m. UTC | #6
On 3/9/2026 6:33 AM, daichengrong wrote:
>
>
> On 3/7/26 00:42, Adhemerval Zanella Netto wrote:
>
> >
> > Sorry, but this kind of code duplication for arch-specific 
> workarounds are not
> > acceptable.
>
> I agree with this point. Using arch-specific code duplication as a 
> workaround
> is not a good long-term solution.
>
> In the v3 RFC reference implementation, I first tried to adjust the code
> structure to avoid giving the compiler the opportunity to generate a 
> memset
> libcall. The idea was to explore this from the perspective of compiler
> optimization behavior, i.e. trying to prevent certain patterns from being
> optimized into calls to more complex libc routines.
That's effectively coding to an implementation detail in the compiler.  
Yea, maybe you can obfuscate things today, but it opens up a maintenance 
burden as glibc is on the path to supporting multiple compilers and 
compilers are continually improving.

In general I recommend avoiding this path, it's proven too fragile in 
many contexts through the decades I've been involved in compiler work.
>
>
> Regarding the option you mentioned about using
> -ftree-loop-distribute-patterns to avoid libcalls,
>
> I am not entirely sure whether disabling this optimization for this TU
> might cause us to lose some optimizations that the compiler would
> otherwise perform, or lead to other unintended effects on the generated
> code.
Any impact would be minimal.  Essentially that option recognizes certain 
loops are just memcpy, memset, etc and converts the loops to call the 
relevant function.  Within this TU and in the context of early startup, 
you never want the compiler to make those kinds of optimizations as they 
introduce the precise behavior we're trying to avoid.

The biggest problem with using the flag is it's specific to a particular 
compiler, GCC in this case.


>
> For example, in the RFC v3 reference implementation, a memcpy was used 
> even
> though the number of bytes to copy is known at compile time:
The compiler has the choice to inline expand or make calls to memcpy, 
memset, etc.  Each target in the compiler has influence on that decision 
-- ie, one target might decide to inline expand (and thus would be safe) 
while another might call the library routine. Worse yet, those decisions 
could change from one release of the compiler to the next, etc.

That's why we want the dl-symbol-redir-ifunc approach.  It works 
irrespective of the compiler's decisions.

jeff
  

Patch

diff --git a/sysdeps/riscv/cpu-features.c b/sysdeps/riscv/cpu-features.c
new file mode 100644
index 0000000000..56f694c31f
--- /dev/null
+++ b/sysdeps/riscv/cpu-features.c
@@ -0,0 +1,88 @@ 
+/* Initialize CPU feature data.
+   This file is part of the GNU C Library.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+# pragma GCC visibility push(hidden)
+#include <assert.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+#include <dl-machine.h>
+#include <dl-debug.h>
+
+#define RESOLVE_MAP(map, scope, sym, version, flags) map
+#include "elf/dynamic-link.h"
+#include "elf/get-dynamic-info.h"
+
+/* Relocate static executable with PIE for local ifunc symbol.  */
+static void
+init_cpu_features (void)
+{
+  ElfW(Addr) file_p_vaddr = 0;
+  const ElfW(Phdr) *ph, *phdr = GL(dl_phdr);
+  size_t phnum = GL(dl_phnum);
+
+  struct link_map *map = _dl_get_dl_main_map ();
+
+  for (ph = phdr; ph < &phdr[phnum]; ++ph)
+    switch (ph->p_type)
+      {
+      case PT_LOAD:
+	/* Skip the empty PT_LOAD segment at offset 0.  */
+	if (ph->p_filesz != 0 && ph->p_offset == 0)
+	  file_p_vaddr = ph->p_vaddr;
+	break;
+      case PT_DYNAMIC:
+	map->l_ld_readonly = (ph->p_flags & PF_W) == 0;
+	break;
+      default:
+	break;
+      }
+
+  /* Figure out the run-time load address of static PIE.  */
+  ElfW(Addr) l_addr = elf_machine_load_address ();
+  map->l_addr = l_addr - file_p_vaddr;
+
+  map->l_ld = ((void *) l_addr + elf_machine_dynamic ());
+
+  elf_get_dynamic_info (map, false, true);
+
+  if ((map)->l_info[DT_PLTGOT] != NULL && (map)->l_info[DT_PLTGOT]->d_un.d_ptr != 0)
+  {
+    ElfW(Rela) *reloc = (ElfW(Rela) *)(map->l_info[DT_JMPREL]->d_un.d_val + map->l_addr);
+    ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
+
+    ElfW(Addr) * start = (ElfW(Addr) *)(gotplt + 2);
+    size_t rela_size = map->l_info[DT_PLTRELSZ]->d_un.d_val;
+    size_t num_plt_entries = rela_size / 8;
+
+    ElfW(Addr)  plt_addr  = (ElfW(Addr))((ElfW(Addr) *)(*start) + 4) + (ElfW(Addr))map->l_addr;
+    for (int ranges_index = 0; ranges_index < num_plt_entries; ++ranges_index)
+      {
+        ElfW(Addr) r_info = reloc->r_info;
+        if(ELFW (R_TYPE) (r_info) == R_RISCV_IRELATIVE)
+        {
+          ElfW(Addr) addr = (ElfW(Addr))map->l_addr + reloc->r_addend;
+          *start = ((ElfW(Addr) (*) (uint64_t, void *, void *)) (addr))(0, NULL, NULL);
+        }
+
+        start++;
+        reloc ++;
+        plt_addr += 16;
+      }
+  }
+}
diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
index 8c7312ad98..dfb202157c 100644
--- a/sysdeps/riscv/dl-machine.h
+++ b/sysdeps/riscv/dl-machine.h
@@ -1,5 +1,5 @@ 
 /* Machine-dependent ELF dynamic relocation inline functions.  RISC-V version.
-   Copyright (C) 2011-2026 Free Software Foundation, Inc.
+   Copyright (C) 2011-2025 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -26,7 +26,7 @@ 
 #include <sys/asm.h>
 #include <dl-tls.h>
 #include <dl-irel.h>
-#include <dl-static-tls.h>
+#include <elf/dl-static-tls.h>
 #include <dl-machine-rel.h>
 
 #ifndef _RTLD_PROLOGUE
diff --git a/sysdeps/riscv/libc-start.c b/sysdeps/riscv/libc-start.c
new file mode 100644
index 0000000000..5584ce165f
--- /dev/null
+++ b/sysdeps/riscv/libc-start.c
@@ -0,0 +1,34 @@ 
+/* Override csu/libc-start.c on riscv.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+# if ENABLE_STATIC_PIE
+#ifndef SHARED
+/* Relocate local IFUNC symbols in static PIE for early self relocation. */
+# if BUILD_PIE_DEFAULT
+#  pragma GCC visibility push(hidden)
+# endif
+
+# include <startup.h>
+# include <ldsodefs.h>
+# include <cpu-features.c>
+
+# define ARCH_INIT_CPU_FEATURES() init_cpu_features()
+
+#endif /* !SHARED */
+#endif
+#include <csu/libc-start.c>