These mainly concern relocation handling. This introduces a way to
represent most relocation types---those which write to a single
bit-field in a 8/16/32/64-bit backing integer---in a target-agnostic
manner, without tons of copy-and-pasted logic. It isn't quite as
generalized as what GNU ld does, but it can still handle *most* common
cases, and it represents these cases in 16 bits of state without any
lookup table.
I've documented the new relocation types in detail in doc comments on
`SymbolReloc.Type`, so take a look at those if you're interested. Also
see the new code in `addRelocAssumeCapacity`, which is responsible for
mapping the ELF relocation enums to this system.
The logic for emitting runtime relocations has been greatly simplified.
It no longer requires any target-specific logic, because even on targets
with many complex static relocations, there are usually only a handful
of dynamic relocations, so the code emitting them can quite easily be
abstracted across target architectures.
More generally, target-specific logic has been cleaned up and pulled
together to make it easier to add support for new targets. For instance,
a new function `targetPltInfo` is introduced which just returns a bunch
of information about the structure of the PLT on this particular target.
(I also filled in a bit more target-specific logic, e.g. lowerings for a
few relocations and some missing cases in `MachineRelocType`.)
`ehdrField` is replaced with more specialized functions, which return
slightly-modified enum types with impossible tags omitted. This makes it
much easier to use exhaustive `switch` statements in the linker when
branching on things like the target ELF machine.
The linker now has basic detection and error reporting for misaligned or
overflowed relocation values. The error reporting isn't very good yet
(you just get told that the overflow/misalignment happened and for how
many relocations), but it's there!
There are probably a few other smaller refactors and bugfixes here which
I don't remember. Awfully sorry to throw all of this in one commit, I
kept finding yaks to shave mid-way through the relocation type stuff!
Resolves: https://codeberg.org/ziglang/zig/issues/36066
On 32-bit systems usize is not u64 but it was assumed in multiple places
of the build script.
Sometimes using u64 instead of usize since available and used memory can
exceed usize on 32-bit systems (just like totalSystemMemory)
Example errors:
lib/compiler/build_runner.zig:508:26: error: expected type 'usize', found 'u64'
.available_rss = max_rss,
^~~~~~~
lib/compiler/build_runner.zig:508:26: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
referenced by:
callMain [inlined]: lib/std/start.zig:699:88
callMainWithArgs [inlined]: lib/std/start.zig:638:20
posixCallMainAndExit: lib/std/start.zig:590:38
2 reference(s) hidden; use '-freference-trace=5' to see all references
lib/std/Build/WebServer.zig:849:38: error: expected type 'usize', found 'u64'
const buf = gpa.realloc(old_buf, new_len) catch @panic("out of memory");
^~~~~~~
lib/std/Build/WebServer.zig:849:38: note: unsigned 32-bit int cannot represent all possible unsigned 64-bit values
lib/std/mem/Allocator.zig:399:58: note: parameter type declared here
pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(old_mem) {
^~~~~
build.zig:548:10: error: type 'usize' cannot represent integer value '9300000000'
.max_rss = 9_300_000_000,
~^~~~~~~~~~~~~~~~~~~~~~~
build.zig:778:10: error: type 'usize' cannot represent integer value '8000000000'
.max_rss = 8_000_000_000,
~^~~~~~~~~~~~~~~~~~~~~~~
Make shared_objects a StringArrayHashMap so that deduping does not
need to happen in flush. That deduping code also was using an O(N^2)
algorithm, which is not allowed in this codebase. There is another
violation of this rule in resolveSymbols but this commit does not
address it.
This required reworking shared object parsing, breaking it into
independent components so that we could access soname earlier.
Shared object parsing had a few problems that I noticed and fixed in
this commit:
* Many instances of incorrect use of align(1).
* `shnum * @sizeOf(elf.Elf64_Shdr)` can overflow based on user data.
* `@divExact` can cause illegal behavior based on user data.
* Strange versyms logic that wasn't present in mold nor lld. The logic
was not commented and there is no git blame information in ziglang/zig
nor kubkon/zld. I changed it to match mold and lld instead.
* Use of ArrayList for slices of memory that are never resized.
* finding DT_VERDEFNUM in a different loop than finding DT_SONAME.
Ultimately I think we should follow mold's lead and ignore this
integer, relying on null termination instead.
* Doing logic based on VER_FLG_BASE rather than ignoring it like mold
and LLD do. No comment explaining why the behavior is different.
* Mutating the original ELF symbols rather than only storing the mangled
name on the new Symbol struct.
I noticed something that I didn't try to address in this commit: Symbol
stores a lot of redundant information that is already present in the ELF
symbols. I suspect that the codebase could benefit from reworking Symbol
to not store redundant information.
Additionally:
* Add some type safety to std.elf.
* Eliminate 1-3 file system reads for determining the kind of input
files, by taking advantage of file name extension and handling error
codes properly.
* Move more error handling methods to link.Diags and make them
infallible and thread-safe
* Make the data dependencies obvious in the parameters of
parseSharedObject. It's now clear that the first two steps (Header and
Parsed) can be done during the main Compilation pipeline, rather than
waiting for flush().