[llvm-branch-commits] [lld] ELF: Only rewrite non-preemptible IFUNCs to IPLT functions if a non-IRELATIVE relocation is needed. (PR #133531)
Peter Collingbourne via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Apr 10 12:42:04 PDT 2025
pcc wrote:
> How does this work in the non-PIE (PDE) case when we take the address of an ifunc and pass it to a function in a shared library, which then compares the argument with its own global address take of the ifunc?
>
> For example: shared lib
>
> ```
> typedef void Fptr(void);
> extern void ifn(void);
>
> // take address of ifunc ifn defined in application
> Fptr* ifp = &ifn;
>
> // compare address of ifn we have calculated in ifp vs
> // address calculated by application, passed in fp1.
> int compare(Fptr* fp1) {
> return fp1 == ifp;
> }
> ```
>
> App
>
> ```
> typedef void Fptr(void);
> extern int compare(Fptr* fp1);
> int val = 0;
> static void impl(void) { val = 42; }
> static void *resolver(void) { return impl; }
> __attribute__((ifunc("resolver"))) void *ifn();
>
> extern Fptr* fp;
>
> int main(void) {
> return compare(fp);
> }
> // separate file so compiler is unaware ifn is an ifunc.
> typedef void Fptr(void);
> extern void ifn(void);
> Fptr* fp = &ifn;
> ```
>
> Right now in the application lld produces an iPLT entry for `ifn`, with `fp` pointing to the iPLT entry. The dynamic symbol table contains the address of the iPLT entry with type STT_FUNC . The shared library and the argument compare equal.
>
> As I understand it, this patch will change `fp` to point directly to the result of the ifunc resolver. So unless we also change the value put into the dynamic symbol table we'll stop comparing equal.
>
> I don't think there's a STT_FUNC symbol we can put in the dynamic symbol table that holds the result of the ifunc resolver. GNU ld, puts the address of the resolver function with a STT_GNU_IFUNC symbol type in the dynamic symbol table. If that causes the dynamic loader to call the resolver and replace the value with the result then that would work. I haven't had time to check what glibc does though.
If a dynamic loader sees a symbol with type `STT_GNU_IFUNC` it will indeed call the resolver and use the return value when resolving relocations involving that symbol. You can see this behavior in glibc e.g. `elf_machine_rela` function in `sysdeps/aarch64/dl-machine.h` and bionic in the `resolve_symbol_address` function in `bionic/linker/linker_soinfo.h`. So in your example we will end up with `resolver` being called twice: once for the `R_AARCH64_ABS64` in the `ifp` initializer and once for the `R_AARCH64_IRELATIVE` in the `fp` initializer. So the comparison should still succeed.
https://github.com/llvm/llvm-project/pull/133531
More information about the llvm-branch-commits
mailing list