When the member is initialized in the constructor body, but also has a
default constructor, you pay for default construction to just throw it
away. In this case a StringView is light weight to initialize, but we
might as well fix all cases we find.
Found by PVS-Studio: https://pvs-studio.com/en/docs/warnings/v818/
Consider the situation where two shared libraries libA and libB, both
depending (as in having a NEEDED dtag) on libC. libA is first
dlopen()-ed, which produces libC to be mapped and linked. When libB is
dlopen()-ed the DynamicLinker would re-map and re-link libC though,
causing any previous references to its old location to be invalid. And
if libA's PLT has been patched to point to libC's symbols, then any
further invocations to libA will cause the code to jump to a virtual
address that isn't mapped anymore, therefore causing a crash. This
situation was reported in #10014, although the setup was more convolved
in the ticket.
This commit fixes the issue by distinguishing between a main program
loading being performed by Loader.so, and a dlopen() call. The main
difference between these two cases is that in the former the
s_globals_objects maps is always empty, while in the latter it might
already contain dependencies for the library being dlopen()-ed. Hence,
when collecting dependencies to map and link, dlopen() should skip those
that are present in the global map to avoid the issue described above.
With this patch the original issue seen in #10014 is gone, with all
python3 modules (so far) loading correctly.
A unit test reproducing a simplified issue is also included in this
commit. The unit test includes the building of two dynamic libraries A
and B with both depending on libline.so (and B also depending on A); the
test then dlopen()s libA, invokes one its function, then does the same
with libB.
These are found in some libraries, and LibELF doesn't know how to handle
them, not even their name. Adding these definitions should at least help
readelf display information correctly, but more work is needed to
actually implement them.
A copy of the same mapping was found both in LibELF and in the readelf
utility, which uses LibELF; keeping them both is redundant and removing
the duplicate saves (a bit of) space.
The dynamic loader was mistakenly assuming that there are only two types
of program load headers: text (RX) and data (RW).
Now that we're linking with `-z separate-code`, we will also get some
read-onlydata (R) segments. These can be memory-mapped directly without
making a private per-process copy.
To solve this, the code now instead separates the headers into map/copy
instead of text/data. Writable segments get copied, while non-writable
segments get memory-mapped. :^)
GNU ld sometimes generates zero-sized PT_LOAD headers when running with
the "-z separate-code" option. Let's not choke on such headers, we can
just ignore them and move along.
Optimizations:
- Make sure `DT_SYMTAB` is a string view literal, instead of string.
- DynamicObject::HashSection::lookup_sysv_symbol should be using
raw_name() from symbol comparison to avoid needlessly calling
`strlen`, when the StrinView::operator= walks the cstring without
calling `strlen` first.
- DynamicObject::HashSection::lookup_gnu_symbol shouldn't create a
symbol unless we know the hashes match first.
In order to test these changes I enabled Undefined behavior sanitizer
which creates a huge amount of relocations, and then ran the browser
with the help argument 100 times. The browser is a fairly big app with
a few different libraries being loaded, so it seemed liked a good
target.
Command: `time -n 100 br --help`
Before:
```
Timing report:
==============
Command: br --help
Average time: 3897.679931 ms
Excluding first: 3901.242431 ms
```
After:
```
Timing report:
==============
Command: br --help
Average time: 3612.860107 ms
Excluding first: 3613.54541 ms
```
These integer => pointer => integer conversions were technically prone
to UB, since they were used as offsets (which are perfectly fine to be
zero), but we calculated them with pointer arithmetic. This made Clang
insert pointer overflow UBSAN checks, which trigger in case of a zero
result.
When loading libraries, it is required that each library uses the same
instance of each symbol, and that they use the one from the executable
if any. This is barely noticeable if done incorrectly; except that it
completely breaks RTTI on Clang. This switches the hash map to be
ordered; tested to work for Clang by @Bertaland
The System V ABI for both x86 and x86_64 requires that the stack pointer
is 16-byte aligned on entry. Previously we did not align the stack
pointer properly.
As far as "main" was concerned the stack alignment was correct even
without this patch due to how the C++ _start function and the kernel
interacted, i.e. the kernel misaligned the stack as far as the ABI
was concerned but that misalignment (read: it was properly aligned for
a regular function call - but misaligned in terms of what the ABI
dictates) was actually expected by our _start function.
Previously, we assumed that the `.text` segment was loaded at vaddr 0 in
all dynamic libraries, so we used the dynamic object's base address with
`msyscall`. This did not work with the LLVM toolchain, as it likes to
shuffle these segments around.
This now also handles the case when there are multiple text segments for
some reason correctly.
It's perfectly acceptable for the segment's vaddr to not be page aligned
as long as the segment itself is page-aligned. We'll just map a few more
bytes at the start of the segment that will be unused by the library.
We didn't notice this problem because because GCC either always uses
0 for the .text segment's vaddr or at least aligns the vaddr to the
page size.
LibELF would also fail to load really small libraries (i.e. smaller than
4096 bytes).
My previous patch (1f93ffcd) broke loading objects whose first PT_LOAD
entry had a non-zero vaddr.
On top of that the calculations for the relro and dynamic section were
also incorrect.
This implements StringUtils::find_any_of() and uses it in
String::find_any_of() and StringView::find_any_of(). All uses of
find_{first,last}_of have been replaced with find_any_of(), find() or
find_last(). find_{first,last}_of have subsequently been removed.
The LexicalPath instance methods dirname(), basename(), title() and
extension() will be changed to return StringView const& in a further
commit. Due to this, users creating temporary LexicalPath objects just
to call one of those getters will recieve a StringView const& pointing
to a possible freed buffer.
To avoid this, static methods for those APIs have been added, which will
return a String by value to avoid those problems. All cases where
temporary LexicalPath objects have been used as described above haven
been changed to use the static APIs.
This was causing CrashDaemon to choke on our coredumps. Note that we
didn't care about the validation failures before this change either,
this patch simply reorders the checks to avoid divide-by-zero when
validating an ET_CORE file.