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.
* load TLS segment for executable - while we can skip PT_LOAD for executable,
we still have to load TLS segment.
* set TCB address based on if elf is position independent
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.
This patch avoids collecting symbols, resolving relocs if they are
already done (usually for example libc.so during a dlopen for another
libfoo.so). This patch is purely for performance boost.
It is usually not optimal to load a library twice and for specifics,
it is **terrible** idea to load libc twice it was enough trouble
dealing with libc statically linked into ld.so. So What this patch does
it check for soname and if a library is already loaded it won't get
loaded again. Why soname ? because unfortunately some bins gets linked
againt libc.so while of their dependencies gets linked against
libc.so.6 while one is usually symbolic link for the other.
struct flock is posix defined locking mechanism on *nix platform
Example usage (copied from https://gavv.github.io/articles/file-locks/) :
#include <fcntl.h>
struct flock fl;
memset(&fl, 0, sizeof(fl));
// lock in shared mode
fl.l_type = F_RDLCK;
// lock entire file
fl.l_whence = SEEK_SET; // offset base is start of the file
fl.l_start = 0; // starting offset is zero
fl.l_len = 0; // len is zero, which is a special value representing end
// of file (no matter how large the file grows in future)
fl.l_pid = 0; // F_SETLK(W) ignores it; F_OFD_SETLK(W) requires it to be zero
// F_SETLKW specifies blocking mode
if (fcntl(fd, F_SETLKW, &fl) == -1) {
exit(1);
}
// atomically upgrade shared lock to exclusive lock, but only
// for bytes in range [10; 15)
//
// after this call, the process will hold three lock regions:
// [0; 10) - shared lock
// [10; 15) - exclusive lock
// [15; SEEK_END) - shared lock
fl.l_type = F_WRLCK;
fl.l_start = 10;
fl.l_len = 5;
// F_SETLKW specifies non-blocking mode
if (fcntl(fd, F_SETLK, &fl) == -1) {
exit(1);
}
// release lock for bytes in range [10; 15)
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
exit(1);
}
// close file and release locks for all regions
// remember that locks are released when process calls close()
// on any descriptor for a lock file
close(fd);