[PATCH v2] RISC-V: Fix SIGSEGV of --static-pie binaries on riscv64 [BZ #33911]
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Mon Mar 9 13:18:05 GMT 2026
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
More information about the Libc-alpha
mailing list