Skip to content

[AArch64] Fix pointer inequality in pointers to the same ifunc symbol#945

Open
parth-07 wants to merge 1 commit intoqualcomm:mainfrom
parth-07:IFuncPointerEquality
Open

[AArch64] Fix pointer inequality in pointers to the same ifunc symbol#945
parth-07 wants to merge 1 commit intoqualcomm:mainfrom
parth-07:IFuncPointerEquality

Conversation

@parth-07
Copy link
Copy Markdown
Contributor

@parth-07 parth-07 commented Mar 17, 2026

This commit fixes pointer inequality in pointers to the same ifunc symbol. Let's understand the issue in more detail using the below example:

char *foo_impl() { return "foo"; }

static char *(*foo_resolver())(void) { return foo_impl; }

char *foo() __attribute__((ifunc("foo_resolver")));

char *(*foogp)() = foo;

int main() {
  char *(*foop)() = foo;
}

foogp and foop pointers both point to the same function foo. However, their values were different. The root cause was improper handling of the underlying relocations when the symbol is of type ifunc.

char *(*foogp)() = foo; // R_AARCH64_ABS64
// ...
char *(*foop)() = foo; // R_AARCH64_ADR_GOT_PAGE + R_AARCH64_LD64_GOT_LO12_NC

Previously, the linker resolved the R_AARCH64_ABS64 relocation here to the plt slot of foo, let's refer to it as plt[foo], and the GOT-relocations were resolved to the .got.plt slot of foo, let's refer to it as .got.plt[foo]. As a result, foogp stores the address of plt[foo] and foop stores the contents of .got.plt[foo], that is, the address of the resolved function foo. Clearly, the two values are different.

We resolve this issue by creating a got slot for foo when the foo has both an absolute reference and a GOT-reference. The got slot of foo is filled by the linker and stores the address of plt[foo]. With this, both the absolute-reference and got-reference to an ifunc symbol results in the same address. Please note that:

  • we do not create .got slot of foo when we only have an absolute reference to foo because .got slot is unnecessary in this case, and
  • we do not create .got slot of foo when we only have a GOT-reference because in this case we can directly use .got.plt slot and access the function directly without any indirection penalty or the pointer-inequality bug.

Resolves #913

@parth-07 parth-07 force-pushed the IFuncPointerEquality branch 2 times, most recently from db53f97 to 9ac9369 Compare March 17, 2026 12:46

recordGOT(symInfo, G);
symInfo->setReserved(symInfo->reserved() | Relocator::ReserveGOT);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GNU linker creates a igot and igot.plt probably it would be useful to follow the pattern for IFUNC symbols.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can simplify the ifunc implementation too, probably we can reserve the GOT and PLT slots and later remove the GOT slots not needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GNU linker creates a igot and igot.plt probably it would be useful to follow the pattern for IFUNC symbols.

This is interesting. I did not observe this during my experimentation of IFunc functionality with gnu ld. Can you please share which GNU version are you using?

$ aarch64-none-linux-gnu-ld.bfd --version
GNU ld (Arm GNU Toolchain 15.2.Rel1 (Build arm-15.86)) 2.45.1.20251203
Copyright (C) 2025 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
$ llvm-readelf -S 1.bfd.out | grep -i .got
  [20] .got              PROGBITS        000000000001ffb8 00ffb8 000030 08  WA  0   0  8
  [21] .got.plt          PROGBITS        000000000001ffe8 00ffe8 000048 08  WA  0   0  8

This can simplify the ifunc implementation too, probably we can reserve the GOT and PLT slots and later remove the GOT slots not needed.

This would be the same even if we use the normal .got slot instead of the .igot slot, right? I do prefer to have mechanisms in place so that we only create the slots if they are required. Preemptively creating the slots may lead to unnecessary operations, and reverting/removing the slot may silently bring in bugs in the future if we add any new side-effect to the .got slot creation.

@parth-07 parth-07 requested a review from quic-seaswara March 24, 2026 05:21
@parth-07 parth-07 force-pushed the IFuncPointerEquality branch from 9ac9369 to 18af1fd Compare March 27, 2026 12:56
@partaror partaror force-pushed the IFuncPointerEquality branch from 18af1fd to c2f0c2c Compare March 30, 2026 05:33
@partaror partaror requested a review from efriedma-quic March 31, 2026 12:41
@partaror partaror force-pushed the IFuncPointerEquality branch from c2f0c2c to d1cf9a0 Compare March 31, 2026 12:41
@efriedma-quic
Copy link
Copy Markdown
Contributor

Do we need to do anything special to handle dynamic linking, e.g. there's a reference to the function from a different shared library?

@parth-07 parth-07 force-pushed the IFuncPointerEquality branch from d1cf9a0 to a9a66ad Compare April 1, 2026 18:56
@parth-07
Copy link
Copy Markdown
Contributor Author

parth-07 commented Apr 1, 2026

Do we need to do anything special to handle dynamic linking, e.g. there's a reference to the function from a different shared library?

Yes, there would be some differences. We will need to correctly emit dynamic relocations for the IFunc symbols. Currently, we do not correctly emit dynamic relocations for the IFunc functionality in all cases. This being said, .got slot for IFunc symbol that points to the PLT slot of the symbol will be required in the dynamic executable + shared library case as well.

Currently, we only completely support static executables with IFunc functionality.

@parth-07 parth-07 force-pushed the IFuncPointerEquality branch from a9a66ad to 7170dec Compare April 2, 2026 06:27
@partaror partaror force-pushed the IFuncPointerEquality branch from 7170dec to 3e70227 Compare April 2, 2026 17:47
This commit fixes pointer inequality in pointers to the same ifunc
symbol. Let's understand the issue in more detail using the below
example:

```cpp
char *foo_impl() { return "foo"; }

static char *(*foo_resolver())(void) { return foo_impl; }

char *foo() __attribute__((ifunc("foo_resolver")));

char *(*foogp)() = foo;

int main() {
  char *(*foop)() = foo;
}
```

`foogp` and `foop` pointers both point to the same function `foo`.
However, their values were different. The root cause was improper
handling of the underlying relocations when the symbol is of type ifunc.

```
char *(*foogp)() = foo; // R_AARCH64_ABS64
// ...
char *(*foop)() = foo; // R_AARCH64_ADR_GOT_PAGE + R_AARCH64_LD64_GOT_LO12_NC
```

Previously, the linker resolved the R_AARCH64_ABS64 relocation here to
the plt slot of foo, let's refer to it as plt[foo], and the
GOT-relocations were resolved to the .got.plt slot of foo, let's refer
to it as .got.plt[foo]. As a result, foogp stores the address of
plt[foo] and foop stores the contents of .got.plt[foo], that is, the
address of the resolved function foo. Clearly, the two values are
different.

We resolve this issue by creating a got slot for foo when the foo has
both an absolute reference and a GOT-reference. The got slot of foo
is filled by the linker and stores the address of plt[foo].
With this, both the absolute-reference and got-reference to an ifunc symbol
results in the same address. Please note that:

- we do not create .got slot of foo when we only have an absolute reference
  to foo because .got slot is unnecessary in this case, and
- we do not create .got slot of foo when we only have a GOT-reference
  because in this case we can directly use .got.plt slot and access the
  function directly without any indirection penalty or the
  pointer-inequality bug.

Resolves qualcomm#913

Signed-off-by: Parth Arora <partaror@qti.qualcomm.com>
@parth-07 parth-07 force-pushed the IFuncPointerEquality branch from 3e70227 to cfce1c6 Compare April 3, 2026 06:41
case R_AARCH64_ADR_PREL_PG_HI21_NC: {
std::lock_guard<std::mutex> relocGuard(m_RelocMutex);
if (rsym->isIFunc() && config().isCodeStatic())
rsym->setIFuncAbsRef();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure "AbsRef" really captures the rule here... the issue is really whether the reference is direct, i.e. not through the GOT.

Do we also need to handle R_AARCH64_MOVW_UABS_G0 etc.?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Different address in pointers to the same ifunc function

3 participants