[PATCH] elf: Defer all IRELATIVE relocations until after PLT setup (BZ 20673)
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Mon May 11 18:39:29 GMT 2026
On 11/05/26 15:24, H.J. Lu wrote:
> On Mon, May 11, 2026 at 10:41 PM Adhemerval Zanella Netto
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 08/05/26 20:48, H.J. Lu wrote:
>>> On Tue, Apr 28, 2026 at 9:42 PM Adhemerval Zanella
>>> <adhemerval.zanella@linaro.org> wrote:
>>>>
>>>> When a shared library is built with -z lazy and its IFUNC resolver calls
>>>> a PLT function, the dynamic linker can crash. The resolver runs while
>>>> the PLT stubs still hold their raw ELF virtual addresses — l_addr has
>>>> not yet been added — so the call branches to an unmapped address.
>>>>
>>>> The old code deferred IRELATIVE entries only to the end of the relocation
>>>> range currently being processed (via the r2/end2 scan-ahead mechanism in
>>>> elf_dynamic_do_Rel). This was sufficient only when both IRELATIVE and the
>>>> JMP_SLOT entries for the PLT functions it needs are in the same section.
>>>> On x86-64, aarch64, arm, i386 and most other targets, a file-scope
>>>> initialiser of the form
>>>>
>>>> int (*fptr)(void) = some_ifunc;
>>>>
>>>> causes the linker to place R_*_IRELATIVE in .rela.dyn, while JMP_SLOT
>>>> entries for any PLT calls made by the resolver live in .rela.plt.
>>>> Processing .rela.dyn before .rela.plt means the resolver fires before the
>>>> PLT is usable, regardless of where within .rela.dyn IRELATIVE appears.
>>>>
>>>> Fix this by splitting IRELATIVE processing into a separate, explicitly
>>>> deferred pass. In elf/do-rel.h:
>>>>
>>>> - Remove the r2/end2 variables and the post-loop IRELATIVE re-scan from
>>>> elf_dynamic_do_Rel. IRELATIVE entries are now always skipped in the
>>>> non-bootstrap path.
>>>>
>>>> - Add a new elf_dynamic_do_Rel_irelative function that scans a
>>>> relocation range and calls elf_machine_rel/elf_machine_lazy_rel for
>>>> IRELATIVE and ifunc relocations.
>>>>
>>>> In elf/dynamic-link.h, update _ELF_DYNAMIC_DO_RELOC to use a two-phase
>>>> approach for non-bootstrap builds unconditionally (regardless of whether
>>>> ranges[1].size is zero):
>>>>
>>>> Phase 1+2: elf_dynamic_do_Rel over .rela.dyn then .rela.plt — processes
>>>> everything except IRELATIVE/STT_GNU_IFUNC.
>>>> Phase 3+4: elf_dynamic_do_Rel_irelative over .rela.dyn then .rela.plt —
>>>> processes only IRELATIVE, by which point all PLT stubs are
>>>> valid.
>>>>
>>>> This guarantees that IRELATIVE resolvers can call PLT stubs safely
>>>> regardless of which section the linker placed R_*_IRELATIVE in.
>>>>
>>>> Add ELF_MACHINE_IRELATIVE to the architectures that were missing it so
>>>> the new skip logic in elf_dynamic_do_Rel is compiled for all targets.
>>>>
>>>> I checked on all ABI that support iFUNC (x86_64, i686, aarch64, arm,
>>>> loongarch, powerpc, riscv, s390, and sparc), although on some through
>>>> qemu-system (which should not matter for this case).
>>>>
>>>> It also fixes the mold reported issues [1], which shows an example
>>>> where IFUNC relocation placement and processing can works different
>>>> for different ABIs.
>>>
>>> Just to be clear. This patch doesn't fully fix:
>>>
>>> https://sourceware.org/bugzilla/show_bug.cgi?id=20673
>>>
>>> This fix only deals with IRELATIVE relocation order within an object.
>>> Calling an external function from an IFUC resolver may still crash.
>>> Am I correct?
>> Yes, it does not solve all the iFUNC raised by Szabolcs [1], nor the
>> original BZ#21041 issues [2] which Fangrui creates an example [3]. For this
>> I think we will need something like what Florian did [4], and I am exploring
>> a solution similar.
>>
>>
>> [1] https://sourceware.org/legacy-ml/libc-alpha/2015-11/msg00108.html
>> [2] https://sourceware.org/bugzilla/show_bug.cgi?id=21041
>> [3] https://maskray.me/blog/2021-01-18-gnu-indirect-function
>> [4] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/fw/bug21242
>
> IFUNC resolvers have many limitations when they call another function.
> I don't think we can make it work in all cases. Can you remove the
> reference to BZ 20673 in your commit message? Your patch supports
> any IRELATIVE relocation orders. It should be good enough on its own.
Indeed, albeit I think there are still room for improvements. From Szabolcs
raised points, I think at least we can make point 1 (calling extern functions
from an IFUNC resolver may not work) to work more reliable. I am working on
a patch that should fix it.
The point 2 (-fstack-protector-all may crash a resolver) could be resolved,
although I think it would be complex for ABIs that places the cookie on the TCB
(since we need to know the static TLS size prior sizing the TCB header). I think
a better approach would to warn and disable this by the compiler.
The point 3 (libc functions are not safe to call from resolvers under static
linking) could also be resolved, but I haven't dig into yet. Most likely it will
require adjust the static linking initialization order.
The point 4 (limited methods for an IFUNC resolver to learn about the machine)
is somewhat out of scope of IFUNC implementation. However, I think if we can
make 1. work properly the resolve can rely on calling external library
to gather better information.
The Point 5 (caching results in userspace is unsafe / causes dirty pages) is
an implementation detail of libgcc, so I think it is out-of-scope for the glibc.
The Point 6 (lazy binding requires the resolver to be async-signal-safe and
thread-safe) is also quite hard and I think it falls on same category of
atfork handlers; where glibc can not really enforce a program model
without some compiler help.
I will drop the bug report from commit message, should I send another version?
More information about the Libc-alpha
mailing list