mirror of
https://github.com/rui314/mold.git
synced 2024-12-26 18:02:30 +03:00
temporary
This commit is contained in:
parent
f554ce96e7
commit
94c6917f95
69
BUGS.md
69
BUGS.md
@ -3,44 +3,47 @@ development of the mold linker.
|
||||
|
||||
## GNU IFUNC
|
||||
|
||||
A statically-linked "hello world" program mysteriously crashed in
|
||||
`__libc_start_main` function which is called just after `_start`.
|
||||
Problem: A statically-linked "hello world" program mysteriously
|
||||
crashed in `__libc_start_main` function which is called just after
|
||||
`_start`.
|
||||
|
||||
I opened up gdb and found that the program reads a bogus value from
|
||||
the TLS block. It looks like `memcpy` failed to copy proper data.
|
||||
After some investigation, I noticed that `memcpy` doesn't copy data at
|
||||
all but instead returns the address of `__memcpy_avx_unaligned`
|
||||
function, which is a real `memcpy` function optimized for machines
|
||||
with AVX registers.
|
||||
Investigation: I opened up gdb and found that the program reads a
|
||||
bogus value from a TLS block. It looks like `memcpy` failed to copy
|
||||
proper data there. After some investigation, I noticed that `memcpy`
|
||||
doesn't copy data at all but instead returns the address of
|
||||
`__memcpy_avx_unaligned` function, which is a real `memcpy` function
|
||||
optimized for machines with the AVX registers.
|
||||
|
||||
It turned out the odd issue was caused by the GNU IFUNC mechanism.
|
||||
That is, if a function symbol has the type `STT_GNU_IFUNC`, the
|
||||
function does not do what its name suggests to do but instead returns
|
||||
a pointer to a function that does the actual job. In this case,
|
||||
`memcpy` is an IFUNC function, and it returns an address of
|
||||
`__memcpy_avx_unaligned`.
|
||||
This odd issue was caused by the GNU IFUNC mechanism. That is, if a
|
||||
function symbol has type `STT_GNU_IFUNC`, the function does not do
|
||||
what its name suggests to do but instead returns a pointer to a
|
||||
function that does the actual job. In this case, `memcpy` is an IFUNC
|
||||
function, and it returns an address of `__memcpy_avx_unaligned` which
|
||||
is a real `memcpy` function.
|
||||
|
||||
IFUNC function addresses are stored to `.got` section. The dynamic
|
||||
loader executes all IFUNC functions at startup and replace their GOT
|
||||
entries with their return values. This mechanism allows programs to
|
||||
choose the best implementation among variants of the same function at
|
||||
runtime based on the machine info.
|
||||
IFUNC function addresses are stored to `.got` section in an ELF
|
||||
executable. The dynamic loader executes all IFUNC functions at
|
||||
startup and replace their GOT entries with their return values. This
|
||||
mechanism allows programs to choose the best implementation among
|
||||
variants of the same function at runtime based on the machine info.
|
||||
|
||||
If a program is statically-linked, there's no dynamic loader that
|
||||
rewrites its GOT entries. Therefore, if a program is
|
||||
rewrites the GOT entries. Therefore, if a program is
|
||||
statically-linked, a libc's startup routine does that on behalf of the
|
||||
dynamic loader. Concretely, the startup routine interprets all dynamic
|
||||
relocations between `__rela_iplt_start` and `__rela_iplt_start` symbols.
|
||||
It is linker's responsibility to mark the beginning and the ending of
|
||||
a `.rela.dyn` section with the symbols, so that the startup routine
|
||||
can find it.
|
||||
dynamic loader. Concretely, a startup routine interprets all dynamic
|
||||
relocations between `__rela_iplt_start` and `__rela_iplt_start`
|
||||
symbols. It is linker's responsibility to emit dynamic relocations
|
||||
for IFUNC symbols even if it is linking a statically-linked program
|
||||
and mark the beginning and the ending of a `.rela.dyn` section with
|
||||
the symbols, so that the startup routine can find the relocations.
|
||||
|
||||
The bug was my linker didn't define these symbols. Since these symbols
|
||||
are weak, they are initialized to zero, and from the point of the
|
||||
initializer function, there's no dynamic entries between
|
||||
`__rela_iplt_start` and `__rela_iplt_start` symbols. That left GOT
|
||||
entries for IFUNC symbols. If you call one of the functions, it don't
|
||||
do what it should do but instead returns a pointer that its job.
|
||||
The bug was my linker didn't define `__rela_iplt_start` and
|
||||
`__rela_iplt_stop` symbols. Since these symbols are weak, they are
|
||||
initialized to zero. From the point of the initializer function,
|
||||
there's no dynamic entries between `__rela_iplt_start` and
|
||||
`__rela_iplt_start` symbols. That left GOT entries for IFUNC symbols
|
||||
untouched.
|
||||
|
||||
The proper fix was to define the linker-synthesized symbols. I did
|
||||
that, and the bug was fixed.
|
||||
The proper fix was to emit dynamic relocations for IFUNC symbols and
|
||||
define the linker-synthesized symbols. I did that, and the bug was
|
||||
fixed.
|
||||
|
Loading…
Reference in New Issue
Block a user