Instead of a single source of symbols, now linker keeps a list of DSO (former Library) objects
with their own symbols map. That helps to process R_X86_64_COPY relocations correctly.
For example, if 'a.out' executable with dependencies ['libstdc++.so', 'libc.so'] is being loaded
and 'a.out' uses 'stdout' symbol from 'libc.so', its relocation process goes as follows:
- linker processes relocation entry 'stdout' of type R_X86_64_GLOB_DAT from 'libc.so',
- it goes through object list ['a.out', 'libstdc++.so', 'libc.so'] to find first object
that exports 'stdout' symbol. The symbol is in 'a.out' with the value e.g. '0x404070',
- linker sets 'stdout' symbol GOT entry in 'libc.so' to '0x404070',
....
- linker processes relocation entry 'stdout' of type R_X86_64_COPY from 'a.out',
- it goes through object list excluding 'a.out': ['libstdc++.so', 'libc.so']. The symbol is found in 'libc.so',
- linker copies the 'stdout' symbol content from 'libc.so' to memory at address '0x404070' (in 'a.out' object).
Objects are relocated in reverse order they were loaded. So in the example above, linker starts with relocating
'libc.so' and ends with 'a.out'. It is necessary e.g. when linking with 'libstdc++.so' - there are many
relocations which symbols are found in 'libstdc++.so', so they need to be resolved before their contents are
copied to 'a.out'. That also matches GNU ld.so behavior.
It seams that stdout of ld.so is not that much of an issue but actually
it unfortunately is. The major problem here is that sometimes programs
generate header files in stdout (./getmy_custom_headers > header.h) and
we need to keep that cleen. and this is very very popular in gcc.
Current LD_LIBRARY_PATH implementation overwrites the original search
path, which is not the best idea, instead this patch would check
LD_LIBRARY_PATH first and if it didn't find the libraries it is looking
for, then it will search the original search path
This patch implements ld.so code that makes use of both .init_array and
.fini_array. .init_array is fully utilized and is used in the correct
manner. However .fini_array is not used yet although the function that
runs .fini_array exists
At least in linux kernel, assuming that a.out is an elf that is linked
against relibc's own ld.so. When a user attempts `./a.out`, Linux kernel
will map `./a.out`, then map `ld.so` and jump into ld.so entry point.
In relibc ld.so will simply ignore the kernel mapped a.out and create
its own mapping. This patch forces relic ld.so to use the already mapped
`a.out` when ever possible. This would normally have slight performance
improvement (especially that currently relibc doesn't map a.out but
instead copy the data into empty mmaped memory).
The real motivation behind this patch is while impelemnting Runtime
linker debugging protocol for relibc. part of the protocol is ld.so
inseting address of some ld.so managed data structure into .dynamic
seciton of a.out then the debugger would check it there. The thing is
that debuggers have information about the kernel loaded ./a.out and they
check that one specifically which is in our case totally ignored by
relibc.
Introduction:
The original implementation of `relibc_ld_so_start` assumes that
ld.so will always be invoked manually as in "/lib/ld64.so ./a.out"
The problem is regarding this snippet.
if sp.argc < 2 {
eprintln!("ld.so [executable] [arguments...]");
unistd::_exit(1);
loop {}
}
As such, In linux when user types "./a.out" he will recieve the message
ld.so [executable] [arguments...]
This patch makes use of AUXV, specifically AT_ENTRY. When invoking ld.so
manually, AT_ENTRY happens to be the entry point of ld.so. But when
running `./a.out` directly, AT_ENTRY becomes the entry point of `a.out`
this patch compares AT_ENTRY to the entry point of ld.so, if they are
equal only then it will assume that argv[1] is the real program and
adjust the stack, otherwise it will proceed with the stack unadjusted.
Just a small step along the way to reduce the massive wall of spam
every time you compile.
This was done partly automagically with `cargo fix`. The rest was me
deleting or commenting out a bunch of variables. Hope nothing was
important...