If an object file defined foo and its alias foo@VER, references to foo
in a shared object were bound locally, although the definition is
exported as foo@VER, so an executable's copy relocation or interposing
definition didn't reach them. Now foo, if not exported itself, shares
foo@VER's dynamic symbol and is as preemptible as foo@VER.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A shared object's references to an IFUNC that it exports were bound
locally with IRELATIVE relocations, so a definition that interposes the
IFUNC didn't reach them. They now get JUMP_SLOT and GLOB_DAT relocations
against the symbol, as with GNU ld and lld.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The copyrel-relro tests wrote to each buffer and caught SIGSEGV with
setjmp/longjmp to see if it was read-only. They now read(2) from a pipe
into the buffer instead, which fails with EFAULT if the buffer isn't
writable, so the tests no longer need a signal handler.
.openbsd.randomdata holds values such as the stack protector's guard,
which the OpenBSD loader fills with random bytes and which are not
supposed to change afterwards. OpenBSD's GNU ld and lld put the section
into PT_GNU_RELRO so that it becomes read-only after relocation, and
mold now does the same. Upstream lld does it only for ELFOSABI_OPENBSD
inputs, but mold already creates PT_OPENBSD_RANDOMIZE by the section
name alone, so it doesn't check the OSABI.
A static executable had no .dynamic, so -E, --dynamic-list and the
other options that export symbols were silently ignored. Such an
executable now has .dynamic and .dynsym with the exported symbols, as
the options ask. It still has no .interp and runs as before.
-static -shared output, which also lacked .dynamic, now has one.
The build ID in LC_UUID is a hash of the contents, but its version and
variant bits were those of a version-4 UUID, the random kind. They are
now those of version 3, the kind made by hashing, as in ld-prime's
output. Nothing reads these bits; they only label the value. A
-random_uuid UUID stays version 4.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The function writes the build ID into LC_UUID, and returns the page
hashes the code signature is made of, which it computes even when no
build ID is wanted. write_build_id is the name of its counterpart in
the ELF linker. Its comment now explains what the build ID is, and the
comments of the function and of Args::uuid say that -no_uuid leaves
LC_UUID out, as it does, rather than zeroed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
merge_literals binned the literals' hashes into 64 shards one by one
and then filled a hashbrown table for each shard on one thread, meeting
the copies in input order. It now deduplicates them as the ELF linker's
merged sections do: each kind of literal (a segment, section name and
type, as ld-prime merges only within one) has a ConcurrentMap sized for
2/3 occupancy from a HyperLogLog estimate, and every copy is inserted
in parallel, prefetching a few insertions ahead. The copies of an
element race for its entry with an atomic minimum of a rank that orders
them as before, the most aligned first and the first in input order
among equals, so the winners and the output don't change.
is_mergeable_literal becomes has_mergeable_literals, a test of the
section alone; merge_literals skips the elements with relocations
itself.
The pass is up to 30% faster on large links (iTerm2's debug dylib:
9.4ms to 6.6ms), within the noise of the whole link, and the peak
memory is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
link returned a LinkResult, Result<i32, &str>: an exit status, or the
target the inputs are actually for. The status was always 0, as a link
that fails exits in the middle, so link now returns Option<&str>, the
other target if there is one, and the driver returns nothing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Mach-O driver guessed a target, parsed the options for it, and
started over with the right target when the guess was wrong, as the ELF
driver does. But nothing in the Mach-O option parser depends on the
target until it has found the target, from -arch or the input files, so
a wrong guess only parsed everything twice and gave the options'
warnings twice.
parse_args now parses the options once and settles the target, and the
driver calls that target's link function with the result, which first
resolves what depends on the target (resolve_args). The driver no longer
takes an initial target or loops, and a target's link function no
longer reports another target or an exit status: a failed link exits in
the middle, so it returns nothing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The mergeable record's reader and writer chose ARM64_RELOC_* or
X86_64_RELOC_* by the CPU type; they now use the Target trait's
RELOC_UNSIGNED, RELOC_SUBTRACTOR and RELOC_GOTPC.
- delay_refs and lazy_helper_refs built each kind's fixed list of
split-info references in a Vec, with closures computing the
offsets; they now return a static table per kind.
- The bundle hook's embedded object was copied to the heap and leaked
back; its MappedFile now refers to the static bytes.
- parse_object's alive argument, which its one caller always passed
as true, is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Removing macho/util.rs had turned its push16/32/64 into a push variant
of every reader and writer in mold-common's endian.rs, twelve functions
of which the Mach-O linker used three. Appending to a Vec doesn't belong
with reading and writing integers in place, so endian.rs is back as it
was, and push16, push32 and push64 live in macho.rs beside the Mach-O
linker's other little-endian helpers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The comment assumed the reader already knew what frameworks, resources,
images and mergeable libraries are. It now explains them from the
ground up: how a library in a framework finds its data files through
class_getImageName(), why linking it statically, or from
ReexportedBinaries as a debug build does, breaks that, and how the
macOS linker fakes class_getImageName() to work around it, before
coming to mold's implementation.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/elf and tests/macho each had a crate of their own for running
their shell scripts, which did much the same thing. A single mold-tests
crate directly under tests/ now runs both: lib.rs discovers, schedules,
times out and reports the tests, and elf.rs and macho.rs only pick the
targets, the latter booting a simulator device for each simulator
target in turn.
The two runners' options and conventions are made one. Both take
--native, --all, --triple and --cpu, the Mach-O tests' --native now
running the targets the Mac runs directly, its own architecture and
x86_64 under Rosetta, as --host did. The work directory links mold
under every name the scripts use, a log line ending in "skipped" marks
a skipped test, and unavailable targets are reported the same way.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The ELF and Mach-O linkers each had a copy of the same response file
reader. They now share mold-common's expand_response_files. Its macho
flag keeps the Mach-O linker's one difference: an argument starting
with @rpath, @loader_path or @executable_path is a dylib path, not a
response file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Mach-O linker held back the warnings it gave while reading the
options until it knew the parse was for the right target, so that a
link it parsed again for another target gave each one once, and so
that -w and -fatal_warnings applied to them wherever they appeared. The
ELF linker does neither, and the hold is gone from mold-common.
-w, -fatal_warnings and $LD_TREAT_WARNINGS_AS_ERRORS now take effect as
they are read, as the ELF linker's options do, so a warning given while
the options are read obeys only a -w or -fatal_warnings before it, and
a link parsed twice may give it twice. The tests no longer count such
warnings, and give -w and -fatal_warnings before the options that warn.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Moving the Mach-O linker to mold-common's diagnostics left calls such
as mold_common::warn!(...). Each file now imports the macros it uses,
as the ELF linker's files do, and calls warn!, error! and fatal!.
mold-common gains a notice! macro for notice, as out! is for out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Mach-O linker had its own error.rs. It printed names byte for byte
through raw(), which marked each byte that isn't UTF-8 for its own
renderer to write back, and it sorted the messages of parallel passes.
It now reports through mold-common's error module, as the ELF linker
does: names print with a U+FFFD for each byte that isn't UTF-8, and a
message from another thread may come in any order, though never mixed
with another one.
mold-common's error module takes over the warnings held back while the
options are read and notice, which the Mach-O linker uses, and its
demangle flag serves both linkers. display_name moves to symbol.rs.
The tests of diagnostics that named non-UTF-8 bytes now look for
U+FFFD.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
macho/archive_file.rs was a copy of common/archive_file.rs without thin
archive support. The shared reader now does what the copy did better:
it trims the space padding of a BSD short member name, which the ELF
linker kept, skips every __.SYMDEF variant, and returns each member's
date, which the Mach-O linker writes to debug stabs. MappedFile::member
makes a member a Mach-O input named "archive(member)".
Mach-O records had plain integer fields, read and written in the host's
byte order, so the linker assumed a little-endian host as well as
target, and with their natural alignment they were copied out of the
input files, except for a symbol table that happened to be aligned.
Their integer fields are now byte-backed little-endian U16, U32 and
U64, read and written with get and set, as the ELF linker's are in its
target's byte order. Records have alignment one, each checked by a
const assertion as ELF's are, so the symbol table and the relocations
are viewed in the input files, wherever they are.
FileRecord, with record_from_bytes and records_from_bytes, moves from
the ELF linker to mold-common, and both linkers use it. It gains
as_bytes, which the Mach-O linker uses.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Mach-O linker's util module mostly re-exported mold-common's
helpers. Its files now import them from mold-common as the ELF linker's
do, and call the little-endian accessors by their mold-common names
(read_ul32 for read32, and so on).
What it defined itself goes where it belongs: push16/32/64 become
endian's push_ul16/32/64, align_to_mod moves to bits, path_bytes to
path, name_sort_key to the symbol table and reserve_arena to
input_files. sha256 and sha1 are gone; their callers call the sha2 and
sha1 crates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
-random_uuid makes its UUID with Uuid::new_v4, as the ELF linker's
--build-id=uuid does, and a content UUID is stamped as version 4 with
Builder::from_random_bytes instead of by hand; the bits it sets are the
same. The trace files format the UUID with the crate too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
util.rs held helpers of half a dozen kinds. They now live in modules
named for what they do: bits.rs (alignment, bit fields, sign
extension), leb128.rs, bytes.rs (byte strings), path.rs (lexical path
manipulation), mem.rs (huge pages and leaked allocations), and
parallel.rs (SyncUnsafeCell and atomic_or). The LEB128 and byte string
helpers that only the Mach-O linker had move to them too.
random_bytes is gone: its callers call getrandom::fill.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rest of macho/util/demangle.rs, display_name, can't move to
mold-common with demangle_cpp: it prints through the Mach-O linker's
own diagnostics, with its -demangle setting and its raw. It now lives
in error.rs beside them. That leaves util/ with mod.rs alone, which
becomes util.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only the Mach-O linker uses CityHash64, but nothing in it depends on
Mach-O, so it belongs with the other hash functions in mold-common.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
macho/util/ had its own copies of the glob matcher, SipHash, WorkerLocal,
the C++ demangler and a dozen small helpers that mold-common already
has. The Mach-O linker now uses mold-common's, and its util module
re-exports the helpers it used to define.
The two glob matchers differed only in how they read bracket
expressions and backslashes: ld-prime ends a bracket expression at the
first `]` and knows no negation or escapes, and takes a trailing
backslash as itself. GlobBuilder::add_ld_prime adds a pattern read that
way, and add_literal a name that matches itself alone.
is_space moves to mold-common, and the ELF linker uses it too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The port followed libc++'s __murmur2_or_cityhash, which is under the
Apache License with LLVM exceptions. It now follows CityHash v1.0.3 at
https://github.com/google/cityhash, which libc++'s is derived from and
which is under the MIT license as mold is. Its COPYING is included in
the file.
A mergeable record must hash its names as ld-prime does, with libc++'s
std::hash, so the two places where libc++ differs from v1.0.3 are kept
and marked: 4 to 8 bytes are hashed with a 32-bit shift, and z is added
to rather than set for 33 to 64 bytes. The output is unchanged, and a
new test checks it against values from libc++.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Mach-O linker has only two targets, so they don't need a directory
of their own as the ELF linker's twenty do. The target trait is now in
macho/arch.rs, which re-exports the targets from macho/arm64.rs and
macho/x86_64.rs, so crate::arch::Arm64 and crate::arch::X86_64 still
name them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
rustc emits asynchronous unwind tables, which compact unwind can't
encode, so the unwind info of most functions goes to __eh_frame. In a
debug build, that section exceeds the 16 MiB that compact unwind can
refer to, and Apple's ld warns that exception handling may be slower.
rustc reports the linker's output as a linker_messages warning, so we
allow that lint in debug builds on Apple hosts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The test linked mold into a toolchain's bin directory as ld, but mold
acts as the Mach-O linker only under the name ld64.mold, so the copy
ran the ELF linker and failed on -arch. The test checks that the
linker loads the libLTO in lib beside its bin directory, which doesn't
depend on the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The ELF test harness took the host's compiler for a native ELF target,
and on macOS `cc -dumpmachine` reports arm64-apple-darwin, which it
read as 32-bit arm. It then ran the whole ELF suite with Apple's clang,
which builds Mach-O programs, and most tests failed. The integration
test now skips the ELF tests on macOS, as the Mach-O integration test
skips its tests on other hosts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
e_shnum is a 16-bit field, but its values from SHN_LORESERVE (0xff00)
up are reserved. With that many sections or more, e_shnum must be 0
and the real number goes into the first section header's sh_size. We
did that only past 65535 sections.
Now that the Mach-O linker is part of mold, its banner no longer says
mold-macho, and it carries the commit hash as the ELF linker's does.
The -sdk_imports report names the linker mold-<version>. The tests
tell mold from Apple's ld with a new is_mold helper in common.inc,
which looks for the banner.
elf/build.rs looked up the commit mold is built from and made the ELF
linker's version string from it. The Mach-O linker needs the hash as
well, so mold-common's build script now looks it up and exports it as
mold_common::GIT_HASH. The ELF linker's version string is unchanged.
The rest of the Mach-O linker's Unix-only code gets Windows versions.
The output is written with seek_write and created without a file mode.
std gives no file IDs on Windows, so input files are told apart by
their canonical paths. LTO is reported as unsupported on Windows, as
it is for the ELF linker.
The Windows CI jobs now build a Mach-O target as well.
macho/subprocess.rs was a copy of common/subprocess.rs, except that its
signal handler removed the Mach-O linker's output rather than the ELF
linker's temporary file. The Mach-O linker now registers its output
with mold-common's set_tmpfile, so the shared signal handler removes it,
as do the fatal error path and the panic hook.
macho/util/perf.rs was a copy of common/perf.rs without its Windows
code. The only real difference was that the Mach-O linker prints the
timers to stderr, as ld64 prints -print_statistics, so Timers::print
now takes the stream to print to.
std::os::unix::ffi::OsStrExt doesn't exist on Windows. On Unix,
OsStr::as_encoded_bytes returns the same bytes as OsStrExt::as_bytes,
and mold-common's os_str, which the ELF linker uses, converts bytes
back to an OsStr. This is a step toward building the Mach-O linker on
Windows.