bug-binutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug gas/29655] s390x gas generates PC32DBL instead of PLT32DBL for func


From: rui314 at gmail dot com
Subject: [Bug gas/29655] s390x gas generates PC32DBL instead of PLT32DBL for function call
Date: Fri, 07 Oct 2022 08:43:54 +0000

https://sourceware.org/bugzilla/show_bug.cgi?id=29655

--- Comment #7 from Rui Ueyama <rui314 at gmail dot com> ---
It's about pointer equality. C/C++ guarantees that two function pointers are
equal if and only if they are for the same function. For example, an expression
`&printf` in your main program code should yields the same address as an
expression `&printf` in libc.so.

That doesn't seem like a tricky requirement, but it actually is when dynamic
linking is involved. When doing dynamic linking, there are more than one entry
point for the same exported function. Your main program will call `printf` via
the executable's PLT entry for `printf`; on the other hand, libc may call its
own `printf` via its own PLT entry. libc.so can also call `printf` directly by
jumping to the entry point of the function without PLT.

`&printf` can be evaluated to any of these addresses, but the result must be
consistent across all ELF modules for the same process throughout its lifetime.
Otherwise, the pointer equality will not be guaranteed.

For PIC, the linker creates a GOT entry for `printf`, and the code will read
the entry value to obtain the address of `printf`. If all ELF modules are PIC
(shared objects are always position-independent, so that means the main
executable code was compiled with -fPIC), it's just as simple as the dynamic
linker fills all GOT entries for `printf` with the same address, which is the
real address of the `printf` function in memory.

However, if the main executable wasn't compiled with -fPIC, the main executable
assumes that all variables and functions exist at fixed locations in memory.
That's a wrong assumption for imported functions such as `printf` though. So,
in that case, the static linker makes the dynamic linker to use a main
executable's PLT entry as an address of an imported function. The dynamic
linker sets addresses of the main executable's PLT entries to shared libraries'
GOT entries for pointer equality.

A main executable's PLT entry that is used as a function address is often
called a "canonical PLT".

If the main executable isn't compiled as PIC, and if it takes a pointer of
imported function `foo`, the linker has to make the main program's `foo`'s PLT
entry canonical. If it's not canonical, the main executable would use its own
PLT entry as `foo`'s address, while other shared object files in memory would
use `foo`'s real address of its address, which breaks the pointer equality
guarantee.

Now, we can ask this question: is it safe to always make main executable's PLT
entries canonical? I originally thought that the answer would be yes. Even if
the main program is compiled with -fPIC, it seems that we can still make
`foo`'s PLT entry canonical, so that the PLT entry's address will be used as
`foo`'s address throughout the program execution.

The above logic wasn't wrong, but in reality, there are libraries that assume
the linker doesn't make PLT entries canonical unless necessary. Qt5 is one of
such libraries. It's `connect` function takes a pointer to a callback function.
Internally, it compares a given pointer value with a list of pointers. When
doing so, it looks like Qt compares a given pointer with addresses of symbol
aliases. It works as long as both pointers directly point to the address of a
callback function. But it won't when a given pointer points to a PLT entry of
the main executable. That caused a mysterious runtime issue. So I made a change
to mold so that it makes PLT entries canonical only when needed (i.e. only when
a function address is taken, as opposed to function is just called.)

So, long story short, the linker has to distinguish address-taking relocations
from non-address-taking relocations to properly make/not make PLT entries
canonical. We assume that R_*_PLT* relocations are not address-taking (we
assume such relocations are used for instructions such as x86's CALL, and
despite its name, R_*_PLT* relocations don't request the linker to always
generate PLT entries), and other relocations are address-taking. I want s390x
to follow that convention.

-- 
You are receiving this mail because:
You are on the CC list for the bug.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]