Compare commits

..
2001 Commits
Author SHA1 Message Date
Rui Ueyama 8de38c35a2 Bump mold version to 3.0.0 2026-10-05 15:40:40 +09:00
Rui Ueyama 602d8b42ad Update mold.1 (automated commit) 2026-10-05 05:51:49 +00:00
Rui Ueyama 6e322219ca Fix tests that fail or are needlessly skipped on some hosts
Running the suite on GCC Compile Farm machines found test scripts that
don't work as intended on some hosts:

- on_qemu() took KVM guests on POWER for qemu-user
- the native POWER10 target ignored CPU and shared ppc64le's directory
- readelf 2.35 appends "[<localentry>: 1]" to POWER10 function names
- compressed debug sections hid .debug_str from `readelf -p`
- some tests assumed Linux, glibc, GCC-only flags or g++
- an x86-64 executable was run without $QEMU on a non-x86 host
- old binutils lack multiple .symver and SHF_LINK_ORDER
- GCC 8's `gcc -r` links start files into relocatable output
- debug-macro-section.sh grepped for an offset format objdump doesn't print
2026-10-05 14:41:30 +09:00
Rui Ueyama b4fe61fbf7 Preserve GCC's .debug_macro groups in -r output
With -g3, GCC emits a COMDAT group for the .debug_macro contents of each
header file, and other .debug_macro sections refer to it. mold doesn't
deduplicate these groups because it would resolve references to discarded
copies to 0 (https://github.com/rui314/mold/issues/438), but -r output
dropped the groups while keeping SHF_GROUP on their members. Now -r keeps
every copy as a COMDAT group. GNU ld -r deduplicates them and redirects
the references to the kept copies; without that redirection, mold leaves
deduplication to the final link, where GNU ld, lld and mold handle the
groups as they would in the original objects.
2026-10-05 14:41:30 +09:00
Rui Ueyama b575a42641 Stream relocations in --print-dependencies
--print-dependencies reads the relocations of every input section,
including non-allocated ones whose CREL tables are not decoded when the
file is read. It read them as an array, which reinterpreted such a table
as RELA records and failed an assertion. C++ mold's get_rels() decoded
the table on demand instead.

A read-only pass doesn't need the table materialized, so iterate with
the streaming relocation iterator as --stats does, and document when
the array accessor can be used.
2026-10-05 14:41:30 +09:00
Rui Ueyama 47bf6a030a Compile C code with the medium code model on LoongArch
rustc's LoongArch targets use the medium code model, whose calls reach
±128 GiB, but the cc crate passes no -mcmodel to the C compiler unless
-Ccode-model is given explicitly, so C code got the normal code model,
whose direct calls reach only ±128 MiB. A debug build of mold has more
than 128 MiB of text, so calls from C code such as zstd, mimalloc and
lto-message.c to the PLT failed to link with R_LARCH_B26 overflows on
a native LoongArch host. We now give -Ccode-model=medium explicitly.
2026-10-05 14:41:30 +09:00
Rui Ueyama e2b599c3c1 Preserve non-COMDAT section groups in -r output
A section group whose flag word is 0 is not a COMDAT group. It is never
deduplicated; it only ties its members together so that they are kept or
discarded as a unit. annobin emits such groups, e.g. into crt1.o on
AlmaLinux 8. mold ignored them, but -r output kept SHF_GROUP on their
members, leaving SHF_GROUP sections that no SHT_GROUP lists, which the
gABI forbids and objdump 2.30 rejects. -r output now keeps each such
group with flag word 0, as GNU ld and lld do. Other links still treat
its members as ordinary sections.
2026-10-05 14:41:30 +09:00
Rui Ueyama c4cd78d93a Decode a CREL table on demand when rewriting relocations
CREL tables of non-allocated sections are not decoded when an object
file is read; their relocations are streamed when they are applied.
On ppc64v1, rewrite_opd() also rewrites relocations of non-allocated
sections such as .debug_info, and rels_mut() reinterpreted such a table
as RELA records. Linking a CREL object built with -g failed an
assertion, and a table whose size was a multiple of 24 would be misread.

A CREL table can't be rewritten in its compressed form, so rels_mut()
now decodes it into the side table first, as get_rels() in C++ mold
did. relocation_iter() then returns the rewritten records from there.
2026-10-05 14:41:30 +09:00
Rui Ueyama 63a9f80d1c Drop -e with an address
This reverts aed5a38d1, which made -e accept a number as an entry
address if no symbol has that name, as GNU ld and lld do. New features
are deferred until after 3.0.0.
2026-10-05 14:41:30 +09:00
Rui Ueyama 6c0d688966 Drop ARM64 BTI, PAC and GCS support
This reverts f03b74595, 48428d389 and 3a1b9c6c2, which merged
GNU_PROPERTY_AARCH64_FEATURE_1_AND, emitted BTI PLTs and landing pads
for range extension thunks, and added -z force-bti, pac-plt, gcs,
bti-report and gcs-report.

GCC 16 no longer records -mbranch-protection in .note.gnu.property but
in AArch64 build attributes, which we don't read, so outputs silently
lost the BTI, PAC and GCS bits and -z force-bti warned about every
object file. We'll bring the feature back with build attribute support
after 3.0.0. Until then, ARM64 outputs have no .note.gnu.property, as
in 2.42.1.
2026-10-05 14:41:30 +09:00
Rui Ueyama 1e66190ff1 Use unstable sorts where the C++ version did
These sorts have keys that are unique among the elements, so a stable
sort produces the same order. Using an unstable sort, as C++ mold did,
avoids the stable sort's buffer and signals that ties don't matter.
2026-10-05 14:41:30 +09:00
Rui Ueyama 89148f0a36 Sort ARM32 mapping symbols stably
Two mapping symbols can share an offset, e.g. when `ld -r` places a
section ending with a mapping symbol before one starting with another.
The later one in the symbol table applies, as in the C++ version, which
used stable_sort. An unstable sort could leave BE8 code unswapped.
2026-10-05 14:41:30 +09:00
Rui Ueyama 3da398023e Use the uuid crate for --build-id=uuid
Default features are turned off because uuid's std feature refers to
optional wasm32-only crates, and Cargo puts those into Cargo.lock even
though nothing builds them. With them off, uuid is the only new package.
2026-10-05 14:41:30 +09:00
Rui Ueyama 596475ee28 Simplify 2026-10-05 14:41:30 +09:00
Rui Ueyama 59d20ed5ea Remove redundant instruction and section wrappers 2026-10-05 14:41:30 +09:00
Rui Ueyama e819287122 Remove comments from target instantiation crates 2026-10-05 14:41:30 +09:00
Rui Ueyama b3307426e8 Require a Git clone in dist.sh
When run in an extracted source archive, dist.sh cloned the release tag
as a bare repository into .git to get the commit hash and timestamp.
Cargo refuses to build in a directory whose .git is a bare repository,
and the embedded hash named the tag rather than the files actually
built. A worktree doesn't work either, because its .git file points
outside the build container and the hash is silently omitted. Stop with
an error unless .git is a directory.
2026-10-05 14:41:30 +09:00
Rui Ueyama 56f1250f77 Merge pull request #1702 from aokblast/fbsd/fix_checkl
Fix FreeBSD -B semantic
2026-10-04 09:37:46 +09:00
ShengYi Hung cb9ba2a6a5 Fix FreeBSD -B semantic
FreeBSD defaults to ld.lld. To use -B on cc with mold, we need to
replace link of ld.lld also.
2026-10-04 04:55:42 +11:00
Rui Ueyama 8041014c3e Resolve non-allocated merged sections in the foreground
Non-allocated strings were merged in a background Rayon task, but Rayon
has no task priorities: a thread waiting for a foreground parallel pass
could pick up a long piece of the merge, and the pass waited up to
200 ms for it. Resolve these sections in compute_section_sizes as before
2316c952a. Within noise on large debug builds (0.94-1.04).
2026-10-03 20:36:52 +09:00
Rui Ueyama ffad459a57 Share dummy symbols between relocation pairs to the same fragment
Instruction pairs such as ADRP+ADD on ARM64 refer to the same string
with two consecutive relocations, and each got a dummy symbol of its own.
Reuse the previous relocation's dummy symbol if it refers to the same
section symbol with the same addend. Since both relocations of a pair now
refer to one symbol, ADRP+ADD relaxation applies to them too. On
chrome-release-arm64, reattach_section_pieces 39 -> 30 ms and peak RSS
5.86 -> 5.56 GB.
2026-10-03 20:36:52 +09:00
Rui Ueyama 6a44075396 Lay out thunk-bearing sections over arrays of member attributes
Range extension thunk creation and removal lay out input sections in a
serial loop that looked up each member through its file, costing a few
dependent cache misses per section. Read sizes and alignments into an
array in parallel first, and publish the offsets in parallel. On
chrome-release-arm64, compute_section_sizes 34 -> 27 ms and
remove_redundant_thunks 25 -> 15 ms; on chrome-debug-arm64, 87 -> 81 ms
and 72 -> 50 ms.
2026-10-03 20:36:44 +09:00
Rui Ueyama fb20353ea4 Group identical CIEs by their digests in parallel
CIE deduplication, which both ICF and .eh_frame layout run, compared each
CIE with every distinct CIE found so far in a serial loop. Compute their
SipHash digests in parallel with a random key and number the classes
through a hash table instead, assuming that digests never collide as ICF
does. FDE offsets are now assigned in parallel too. uniquify_cies 9 -> 2
ms on chrome-release-arm64; .eh_frame layout 15 -> 8 ms on
chrome-debug-x86_64.
2026-10-03 20:36:43 +09:00
Rui Ueyama 93d75ca0ee Set symbol visibility without an atomic read-modify-write
set_visibility() is only called on exclusively owned symbols, but it
updated the bits with a compare-and-swap loop. Take &mut self and write
them directly.
2026-10-03 20:36:43 +09:00
Rui Ueyama a0795de60c Collect merged-section members in parallel
merged_resolve_members visited every file on one thread. Fold the
members of each file in parallel and append the per-thread lists in
file order. About 5 ms on chrome-release-arm64.
2026-10-03 20:36:35 +09:00
Rui Ueyama 06893555e7 Size symbol table shards by estimated distinct names
gather reserved each shard for every recorded occurrence of a name, about
three times the number of distinct names, so the sparse table touched far
more pages than it used. Estimate the distinct names with HyperLogLog
instead. chrome-debug-arm64 peak RSS 26.76 -> 25.91 GB.
2026-10-03 20:36:35 +09:00
Rui Ueyama 78753b0fe4 Build glob literal searchers once per pattern
Pattern::matches called memchr::memmem::find for the literal after each
star, which builds a searcher on every call. Build them when compiling
the pattern instead. apply_version_script on tensorflow-debug 260 -> 220
ms.
2026-10-03 20:36:35 +09:00
Rui Ueyama d088db65b1 Set import/export bits and parse symbol versions in parallel
compute_import_export collected updates in parallel and applied them on
one thread because Symbol's attribute bits needed &mut. Make the bits an
AtomicU16 so that file-parallel passes can set them through shared
references, testing a bit before writing it as mark() does.
parse_symbol_version now finds versioned symbols in parallel; it still
applies them in input order because one may read a version that another
has just set. compute_import_export on tensorflow-debug 167 -> 9 ms.
2026-10-03 20:36:35 +09:00
Rui Ueyama 4165d0e82e Redirect branches to section offsets to symbols
Assemblers emit a call to a static function in another section as a
branch to a section symbol plus an offset, but a range extension thunk
jumps to the start of a symbol. On RELA targets, give such a branch a
symbol at its destination and a zero addend, as we do for references to
section fragments. This fixes Chromium's ARM64 debug build, which
9b9162146 made an error and 2.42.1 linked into calls to wrong addresses.
2026-10-03 16:22:19 +09:00
Rui Ueyama 100987dd0e Mention the minimum Rust version in README 2026-10-03 15:01:31 +09:00
Rui Ueyama 6bdd637973 Declare the minimum supported Rust version
mold needs Rust 1.95 for std::hint::cold_path and the atomic update
methods, while its dependencies need only 1.85. Declaring it makes Cargo
reject older toolchains with a clear message before building, and lets
the resolver avoid dependency versions that need a newer rustc.
2026-10-03 14:47:19 +09:00
Rui Ueyama e10a25f478 Link libatomic from mold instead of from the mimalloc crate
This reverts 6f8dbb64b. mimalloc uses 64-bit atomics, which GCC
implements as calls into libatomic on hosts such as 32-bit PowerPC.
The need comes from linking mimalloc statically into mold, so the
executable is the right place to add libatomic. Our mimalloc_rust fork
is pinned to the commit before its libatomic change, so the fork
differs from upstream only by the bundled mimalloc version.
2026-10-03 14:32:23 +09:00
Rui Ueyama 9e320c4012 Use mimalloc on sparc64 hosts again
This reverts bd97ebc90. mimalloc's page map still doesn't cover the
sign-extended high half of the sparc64 address space, but mimalloc maps
its arenas at hinted addresses in the low half, and since v3.5.0 a page
outside the map fails to register instead of corrupting memory. On an
UltraSPARC T5 running Linux, mold with mimalloc 3.5.3 passes the native
test suite.
2026-10-01 22:57:43 +09:00
Rui Ueyama 10b162491a Assemble the R_SPARC_OLO10 test input as non-PIC
GNU as rejects %lo(sym) + addend with -K PIC, which GCC passes to it
when it defaults to PIE, as Debian's sparc64 GCC does.
2026-10-01 22:57:43 +09:00
Rui Ueyama 6f8dbb64b2 Let mimalloc_rust link libatomic
mimalloc_rust now links libatomic itself on targets without 64-bit
atomics, so cli/build.rs, which did that for 32-bit PowerPC hosts, is
no longer needed.
2026-10-01 21:31:34 +09:00
Rui Ueyama b7d67b020e Make two tests independent of the system toolchain
readelf before binutils 2.39 can't decode version 9 indices and exits
with an error, so gdb-index-dwarf5-types ignores its exit status as
check_index already does. arch-x86_64-eh-frame-hdr-order links without
the C runtime, whose objects may have FDEs of their own.
2026-10-01 21:09:56 +09:00
Rui Ueyama a247ca27d3 Preserve SHF_LINK_ORDER only for -r
mold handles .ARM.exidx and other users of SHF_LINK_ORDER directly
rather than implementing the flag in general. This reverts 49129fbd5,
which kept SHF_LINK_ORDER sections alive with their linked sections, and
restores the ARM-specific GC edge to .ARM.exidx. 9aacb62c0 now sets
sh_link only in relocatable output, which needs it to preserve the flag.
2026-10-01 21:09:56 +09:00
Rui Ueyama f20f7bdfa7 Index the types that DWARF 5 type units define
GCC emits no pubnames for type units, so the index listed a type only
for the compilation units referring to it by signature. GDB 18 doesn't
follow such a reference when it looks a type up through the index, so
ptype failed for types defined in type units. Read the name of the type
each type unit defines from its DIEs, resolving their section offsets
with input relocations, and add it to the index for the type unit.
2026-10-01 17:57:23 +09:00
Rui Ueyama 56ee0f8c1b Update mimalloc to v3.5.3 2026-10-01 16:57:38 +09:00
Rui Ueyama 17b7b85a88 Suggest -fPIC for PC-relative references to absolute symbols
A non-PIC object referring to an absolute or undefined weak symbol with
a PC-relative relocation, such as AArch64's ADRP, can't be linked into
a position-independent output, but recompiling it with -fPIC fixes that
by going through the GOT. The error message lost that hint when it was
reworded, which broke abs-error on aarch64_be.
2026-10-01 16:57:38 +09:00
Rui Ueyama 97acaca6d2 Make new tests portable to FreeBSD
od -w is a GNU extension, so arch-x86_64-eh-frame-hdr-order failed on
FreeBSD. head and grep -q exit before reading all of their input, so
under pipefail the producer may die of SIGPIPE and fail the pipeline,
which happened to mergeable-entsize-order on FreeBSD.
2026-10-01 16:57:38 +09:00
Rui Ueyama d62424b202 Update mold.1 (automated commit) 2026-10-01 02:09:06 +00:00
Rui Ueyama c544bc7a81 Don't write STT_TLS labels in .text as TLS offsets
Clang gives the STT_TLS type to the labels of TLSDESC code sequences,
which are in .text. With --emit-relocs they are written to .symtab,
where their values were computed as offsets from the start of the TLS
segment, which underflowed. Write the address of an STT_TLS symbol
outside a TLS section as it is.
2026-10-01 10:09:13 +09:00
Rui Ueyama 9b91621465 Report branches to section offsets that need thunks
Assemblers emit a call to a static function as a relocation against a
section symbol plus an offset. A range extension thunk jumps to the
start of a symbol, so taking such a branch through a thunk landed at
the wrong address. Report it as an error instead.
2026-10-01 10:09:13 +09:00
Rui Ueyama 3717460027 Keep all symbols in -r output
Relocations in a relocatable output may refer to local symbols such as
RISC-V's .L labels, so removing them with -x, -X or -s left relocations
pointing to symbol 0. -r now ignores -x and -X, and -s strips only debug
info as -S does.
2026-10-01 10:09:13 +09:00
Rui Ueyama 2ffdd1256c Fix a syntax error in dt-init.sh
`[[ ... -o ... ]]` is a syntax error in bash, so the script aborted at
that line and the exit trap reported success without running any check.
2026-10-01 10:09:13 +09:00
Rui Ueyama 13e6cc0d59 Don't range-check LoongArch TLS_{GD,LD}_PC_HI20
In the extreme code model, the pcalau12i with R_LARCH_TLS_GD_PC_HI20 or
R_LARCH_TLS_LD_PC_HI20 begins a 64-bit sequence whose lu32i.d and lu52i.d
supply the bits beyond ±2 GiB, so the GOT may be farther away than that.
The other *_PC_HI20 relocations were already applied without a range
check, as in GNU ld and lld, but these two rejected such a GOT with an
out-of-range error.
2026-10-01 10:09:13 +09:00
Rui Ueyama 81339386c9 Compute LoongArch *64_PC_HI12 from the pcalau12i's page
In the extreme code model, lu32i.d and lu52i.d follow pcalau12i by 8 and
12 bytes, and the upper 32 bits they set are relative to the pcalau12i's
page. We used page(P - 8) for both, which is the next page for lu52i.d if
the pcalau12i is the last word of a page. The result was wrong whenever
the borrow reached bit 52; e.g. a pcalau12i at 0x100000ffc materialized
0xfff0000080000000 instead of 0x80000000.
2026-10-01 10:09:13 +09:00
Rui Ueyama bd97ebc907 Use the system allocator on sparc64 hosts
mold crashed at startup on sparc64 Linux, which maps user memory at
sign-extended high addresses. mimalloc v3 registers pages in a map that
covers only 48-bit addresses, so the first registration wrote out of
bounds. Until the bundled mimalloc handles that layout, sparc64 hosts use
the system allocator (https://github.com/rui314/mold/issues/1639).
2026-10-01 10:09:13 +09:00
Rui Ueyama 64325c31cc Make the mold-wrapper.so library directory configurable
mold -run looked for mold-wrapper.so in /usr/local/lib/mold unless it was
next to the executable or in ../lib/mold, and install-mold.sh always put
it in $PREFIX/lib/mold, so a package using lib64 or a multiarch libdir
could not make -run find it. The C++ version took the directory from
CMake's libdir. MOLD_LIBDIR now sets it at build time and install time.
2026-10-01 10:09:13 +09:00
Rui Ueyama ee6fb251da Don't emit PREFETCHT0 on x86 targets without SSE
util::prefetch assumed SSE on all x86 targets, but i586-unknown-linux-gnu
has none, so mold died with SIGILL on CPUs such as the Pentium, K6,
Vortex86 and Quark. The x86 prefetch is now compiled only when the target
has SSE. Such a target simply ignores the hint, as other targets without
a prefetch instruction do.
2026-10-01 10:09:13 +09:00
Rui Ueyama 24d32398f5 Make .dynsym and .eh_frame_hdr order independent of the host
Versions of a symbol have the same name, so they tied in the unstable sort
of exported dynamic symbols. Rayon breaks ties with a pseudo-random
generator whose width follows usize, so .dynsym and its hash and version
tables differed between 32-bit and 64-bit hosts. The input order now breaks
the tie. .eh_frame_hdr had the same problem with FDEs for one function,
which are now ordered by their addresses.
2026-10-01 10:09:13 +09:00
Rui Ueyama 99173586d6 Report outputs too large for a 32-bit host
On a 32-bit host, linking an output of 2 GiB or more panicked, because
the doubled mapping length wrapped around and sizes of 4 GiB or more were
truncated to usize. Each panic also left a multi-GiB temporary file. We
now check the size before creating the file and report a fatal error.
The limit is 2 GiB, as no Rust object can be larger than isize::MAX bytes.
2026-10-01 10:09:13 +09:00
Rui Ueyama 3a1b9c6c28 Add BTI landing pads for range extension thunks
A range extension thunk jumps to its destination with `br x16`, which
faults under BTI if the destination doesn't begin with a landing pad, as
is the case for a static function that is only called directly. For such
a function, we now put `bti c; b <function>` at the beginning of the first
thunk after the function's section, which is always within direct branch
range, and other thunks jump there instead, as lld's landing pad thunks do.
2026-10-01 10:09:13 +09:00
Rui Ueyama 48428d3890 Support -z force-bti, pac-plt, gcs, bti-report and gcs-report
-z force-bti and -z pac-plt force the BTI and PAC bits of the output's
GNU_PROPERTY_AARCH64_FEATURE_1_AND, and -z pac-plt also makes PLT entries
authenticate .got.plt entries with autia1716 and sets DT_AARCH64_PAC_PLT.
-z gcs=implicit|never|always controls the GCS bit. -z bti-report and
-z gcs-report diagnose input files lacking the features, and as in lld,
-z force-bti and -z gcs=always imply the warning level.
2026-10-01 10:09:13 +09:00
Rui Ueyama f03b745952 Merge ARM64 feature properties and emit BTI PLTs
We used to merge only x86 properties in .note.gnu.property, so an ARM64
output lost GNU_PROPERTY_AARCH64_FEATURE_1_AND even if all its inputs were
compiled with -mbranch-protection. We now AND-merge it as GNU ld and lld do.
If the output is BTI-enabled, the PLT header and the PLT entries that may be
reached by an indirect branch start with `bti c` as lld's do, which makes PLT
entries 24 bytes long, and DT_AARCH64_BTI_PLT is set.
2026-10-01 10:09:13 +09:00
Rui Ueyama 4ba7792e43 Keep addends of symbol-less relocations in -r output
R_RISCV_ALIGN has no symbol, and its addend is the size of the NOP
padding that a relaxing link shrinks to realign the code after it.
symidx_addend returned (0, 0) for any symbol not written to the output
symbol table, including the null symbol, so `mold -r` wrote R_RISCV_ALIGN
with addend 0 and a later link could no longer realign the code. We now
keep the addend of a relocation without a symbol, as GNU ld and lld do.
LoongArch's R_LARCH_ALIGN refers to a local symbol and was not affected.
2026-10-01 10:09:13 +09:00
Rui Ueyama a36a1611e6 Support LoongArch extreme code model TLSDESC relocations
GCC 14 uses R_LARCH_TLS_DESC64_PC_LO20 and R_LARCH_TLS_DESC64_PC_HI12
with -mcmodel=extreme -mtls-dialect=desc to extend the TLSDESC sequence's
GOT access to 64 bits, and we rejected them as unknown. We now apply them
like the other 64-bit PC-relative pairs and don't rewrite the sequence's
LO12 instruction with pcaddi. As GNU ld and lld do, we don't relax the
sequence to IE or LE, except in static executables, which can't use
TLSDESC and therefore get LE.
2026-10-01 10:09:13 +09:00
Rui Ueyama 1ac5ff77e7 Relax RISC-V GOT loads that follow shrunk instructions
is_got_load_pair checks that a GOT load's PCREL_LO12_I refers to the
label at its AUIPC by comparing the label's value with the AUIPC's
r_offset. Symbol values are adjusted for removed bytes before relocations
are applied, but r_offsets are not, so a GOT load after a shrunk
instruction was no longer rewritten to AUIPC+ADDI. We now compare with
the label's st_value, as find_paired_reloc does.
2026-10-01 10:09:13 +09:00
Rui Ueyama 85d55f3fa4 Check ranges of RISC-V 32-bit PC-relative relocations
R_RISCV_32_PCREL, R_RISCV_PLT32 and R_RISCV_GOT32_PCREL in allocated
sections were truncated to 32 bits without a range check, so a reference
to a symbol or a GOT slot more than 2 GiB away silently got a wrong
value. We now report an out-of-range error for them, as we already do
for R_RISCV_32_PCREL in .eh_frame and as lld does.
2026-10-01 10:09:13 +09:00
Rui Ueyama 767260f53d Skip unknown RISC-V attributes by their tag parity
The .riscv.attributes parser knew the value types of only the three tags
mold uses, so the value of any other tag was read as a tag. For example,
an unknown tag 40 with value 5 was mistaken for Tag_RISCV_arch. As per
the psABI, an even tag has a ULEB128 value and an odd tag has a
NUL-terminated string, so we now skip other attributes by that rule.
They are still dropped from the output, as lld does for unknown tags.
2026-10-01 10:09:13 +09:00
Rui Ueyama 6b60f75821 Merge EF_RISCV_TSO of RISC-V input files
The output's e_flags were the first input's plus EF_RISCV_RVC, so an
object compiled for the TSO memory model (-march=rv64gc_ztso) lost its
EF_RISCV_TSO bit if it came after an ordinary RVWMO object. RVWMO code
runs correctly on TSO hardware but not vice versa, so the output now has
the bit if any input has it. GNU ld does the same; lld still takes the
bit from the first file.
2026-10-01 10:09:13 +09:00
Rui Ueyama f94d51fd53 Match C++ mold's -Map and --print-dependencies output
-Map wrote address zero as "0x0" where C++ mold wrote "0", and
--print-dependencies omitted the blank line that separated the comment
header from the dependency lines. Both outputs are now byte-identical
to those of C++ mold.
2026-10-01 10:09:13 +09:00
Rui Ueyama 8a360ff453 Treat an empty XDG_RUNTIME_DIR as unset for MOLD_JOBS
With MOLD_JOBS=1 and an empty XDG_RUNTIME_DIR, mold joined "" with
"mold-lock" and created the lock file in the current directory, so
processes in different directories did not serialize. Like other XDG
variables, an empty value now means unset, and mold falls back to
/tmp/mold-lock-$USER.
2026-10-01 10:09:13 +09:00
Rui Ueyama db723dfbac Accept any bytes as values of ignored options
-rpath-link, -fuse-ld, -O, --sort-section and --max-cache-size are
accepted only for compatibility, and their values are never used. mold
nevertheless required the values to be UTF-8, so a non-UTF-8 directory
given to -rpath-link was a fatal error. Read the values as raw bytes.
2026-10-01 10:09:13 +09:00
Rui Ueyama 037d1d57e6 Document -z noseparate-code as the default
The manual said -z separate-code was the default, but mold defaults to
-z noseparate-code and adds no padding between segments unless
--section-order is given, which implies -z separate-loadable-segments.
2026-10-01 09:57:41 +09:00
Rui Ueyama 04b493edf1 Make documented option spellings match the parser
The manual documents --no-color-diagnostics, but mold rejected it as an
unknown option; accept it as lld does. Conversely, the manual and --help
listed -z nodefs and -z start_stop_visibility, which neither mold nor
GNU ld accepts; the options are -z undefs and -z start-stop-visibility.
--help also said --color-diagnostics means always, while it means auto.
2026-10-01 09:57:41 +09:00
Rui Ueyama 2ca893e9ab Don't let flag options consume the next argument
--dynamic-list-data, --lto-pseudo-probe-for-profiling and a bare
--thinlto-index-only take no argument in GNU ld and lld, but mold read
the next command line argument as their value, silently dropping an
input file placed there. GNU ld's --package-metadata takes an optional
argument, so mold now accepts its value only after an equal sign.
2026-10-01 09:57:41 +09:00
Rui Ueyama 033d127859 Report I/O errors with strerror text
Rust's io::Error appends " (os error N)" to the strerror text when it is
displayed, so mold printed "cannot open foo.o: No such file or directory
(os error 2)" where the C++ version printed just the text. Diagnostics
now format I/O errors with a strerror helper that returns the text alone
and falls back to the usual formatting for errors not from the OS.
2026-10-01 09:57:41 +09:00
Rui Ueyama 39e6a01e25 Explain why a PC-relative relocation can't be used
scan_pcrel, which also handles GOT-relative relocations, told the user
to recompile with -fPIC whenever it couldn't resolve a relocation, even
though code using GOTOFF is PIC already. It now reports the reason: a
symbol at a fixed address, such as an absolute or undefined weak one, in
a position-independent output, or a preemptible symbol defined in the
shared object being created, which -Bsymbolic or hidden visibility fixes.
2026-10-01 09:57:41 +09:00
Rui Ueyama aed5a38d1d Accept an address as the -e argument
`-e 0x401000` was looked up only as a symbol name, so the link warned
that the entry symbol was undefined and set e_entry to 0. GNU ld and lld
fall back to parsing the argument as a number (decimal, 0x hex or octal)
when no symbol has that name, and so does mold now.
2026-10-01 09:57:41 +09:00
Rui Ueyama b7048f555a Let the first version node win for duplicate names
When a version script named the same symbol in two version nodes, each
exact-name match overwrote the previous one, so the last node won.
GNU ld and lld give the symbol the first node's version (lld warns about
the reassignment, and GNU ld rejects a name that is both local and
global). Apply only the first exact match. Wildcards still let the last
matching node win, as in both linkers.
2026-10-01 09:57:41 +09:00
Rui Ueyama f10b0c40c8 Don't write version sections for outputs without .dynsym
A version script or --default-symver on a link that exports no dynamic
symbols, such as a static executable, made verdef::construct index the
empty .dynsym and panic. Symbol versions exist only for dynamic symbols,
so write no version sections in that case, as GNU ld does.
2026-10-01 09:57:41 +09:00
Rui Ueyama b3566fac1a Report undefined hidden symbols in shared objects
Undefined symbols are allowed by default with -shared or with
--unresolved-symbols=ignore-all, and a non-weak hidden one was then
silently resolved to 0. A hidden symbol can't be resolved at runtime,
so report it as an error regardless of those options, as GNU ld and lld
do. Weak undefined hidden symbols still resolve to 0.
2026-10-01 09:57:41 +09:00
Rui Ueyama 56b862b23c Reject --defsym aliases of shared library symbols
--defsym=foo=bar with bar defined by a shared library copied bar's
library-relative value into an absolute foo, so calling foo jumped to a
bogus address. Such a symbol has no link-time address, so report it the
same way as an undefined one. GNU ld and lld reject it as well.
2026-10-01 09:57:41 +09:00
Rui Ueyama d5781d582c Merge common symbols into the largest one
The first file on the command line owned a common symbol, so `int buf[4]`
in one file and `int buf[64]` in another gave a 16-byte buf, and writes
to it clobbered its neighbours. As GNU ld and lld do, let the largest
common own the symbol and align it to the largest alignment among them,
for .common, .tls_common and -r output alike.
2026-10-01 09:57:41 +09:00
Rui Ueyama abb7b4ef64 Don't create .dynsym in an executable without .dynamic
--export-dynamic and --dynamic-list export symbols even from an
executable that links against no DSO. Such an executable has no
.dynamic, but the symbols were still added to .dynsym, and .dynstr
lacked its leading empty string, which only the creation of .dynamic
adds, so entry 0 of .dynsym got a symbol's name. Like GNU ld and lld,
emit no dynamic symbol table without .dynamic.
2026-10-01 09:57:41 +09:00
Rui Ueyama 696ed71789 Order same-named merged sections by their first member
Mergeable sections that share a name, type and flags but differ in
entry size are merged into separate output sections. Their sort keys
tied, so their order depended on which one the parallel conversion of
input files happened to create first. Break the tie with the first
member's file priority and section index, as for output sections.
2026-10-01 09:57:41 +09:00
Rui Ueyama 0ee71d395c Don't define __start_ and __stop_ symbols for ELF headers
add_synthetic_symbols defined __start_ and __stop_ symbols for every
allocated chunk with a C identifier name, including the ELF and program
headers named EHDR and PHDR. fix_synthetic_symbols skips headers, so
the symbols were left in .symtab with value 0, and -z
start-stop-visibility=protected exported them. Exclude headers in
start_stop_name, which both functions use.
2026-10-01 09:57:41 +09:00
Rui Ueyama ceab12c56b Don't fold exported functions with --icf=safe
compute_address_significance marked exported symbols' sections as
address-taken by walking .dynsym, but .dynsym is not populated until
after ICF, so the loop did nothing and --icf=safe gave identical
exported functions of a DSO the same address. Walk each object file's
defined global symbols instead, whose is_exported bits are already set.
2026-10-01 09:57:41 +09:00
Rui Ueyama c9e4cc4b6e Keep --init and --fini functions under --gc-sections
DT_INIT and DT_FINI refer to the --init and --fini functions, but only
the entry point was a GC root. If such a function was not exported
because it was hidden or made local by a version script, --gc-sections
discarded its section, DT_INIT and DT_FINI were emitted with value 0,
and the DSO crashed on load. Make them GC roots as GNU ld and lld do.
2026-10-01 09:57:41 +09:00
Rui Ueyama a551ec5cc3 Make synthesized ARM32 mapping symbols STT_NOTYPE
The $a, $t and $d symbols that mold creates for PLT entries and thunks
were STT_FUNC, copied from the symbols naming the entries, while AAELF
requires mapping symbols to be STT_NOTYPE. push_mapping_symbol now
builds the symbol from a section index and an address itself.
2026-10-01 09:53:52 +09:00
Rui Ueyama 4c9aad3726 Don't mark big-endian ARM relocatable output as BE8
Only a final link converts big-endian ARM code to BE8, so relocatable
output keeps the input's BE32 code, but it was marked EF_ARM_BE8 anyway.
GNU ld rejected such an object as "already in final BE8 format", and
disassemblers decoded its code in the wrong byte order. -r also failed
with "--be32 is not supported" unless --be8 was given, which GCC and
Clang never pass with -r.
2026-10-01 09:53:52 +09:00
Rui Ueyama 4c26f1649b Write Thumb NOP.W as two halfwords on big-endian ARM
A 32-bit Thumb instruction is a pair of halfwords, each in the byte
order of the input's code, but NOP.W was written as a single 32-bit
word. On big-endian targets, that swapped its halfwords, so a Thumb call
to an undefined weak symbol and a TLSDESC call relaxed to local-exec
became garbage in BE8 output. NOP.W is now written a halfword at a time.
2026-10-01 09:53:52 +09:00
Rui Ueyama c0f9cd6665 Support ARM32 ADR, LDR literal and ABS8/ABS16 relocations
Clang assembles ADR and LDR (literal) referring to a symbol in another
section to R_ARM_ALU_PC_G0 and R_ARM_LDR_PC_G0 in ARM code and to
R_ARM_THM_ALU_PREL_11_0 and R_ARM_THM_PC12 in Thumb code, which failed
with "unknown relocation", as did 8- and 16-bit data. The offset's sign
picks ADD or SUB, or the U bit of LDR, and the Thumb forms are relative
to the PC aligned down to 4, producing the same bytes as GNU ld.
2026-10-01 09:53:52 +09:00
Rui Ueyama 96d02824ee Support R_AARCH64_TSTBR14, ABS16, ABS32 and GOT_LD_PREL19
TBZ/TBNZ to a label in another section, .2byte/.4byte data referring to
a symbol, and GOT loads compiled with -mcmodel=tiny -fPIC failed with
"unknown relocation". TSTBR14 reaches ±32 KiB and is range-checked like
GNU ld and lld do, without thunks. ABS16 and ABS32 are too narrow for
dynamic relocations, so they must be link-time constants in PIC output.
GOT_LD_PREL19 is an LDR literal that loads the symbol's GOT slot.
2026-10-01 09:53:52 +09:00
Rui Ueyama 8233a75df1 Reject --strip-all with --emit-relocs
Relocations copied by --emit-relocs refer to symbols in .symtab, which
--strip-all omits, so the combination crashed or produced relocation
sections pointing to a nonexistent symbol table. Report an error as
lld does. (GNU ld instead keeps the symbols the relocations need.) -r
sets --emit-relocs internally and keeps accepting --strip-all.
2026-10-01 09:53:52 +09:00
Rui Ueyama 9aacb62c03 Set sh_link of SHF_LINK_ORDER output sections
An SHF_LINK_ORDER section's sh_link refers to the section it describes,
but mold always wrote zero. Once passed through -r, such sections lost
their links, so relinking with --gc-sections dropped .ARM.exidx and
__patchable_function_entries, and C++ programs on ARM aborted when
throwing. Set sh_link from the members' linked-to sections. Since it
can name only one section, -r also separates SHF_LINK_ORDER sections
whose linked-to sections are in different output sections, as GNU ld
and lld do.
2026-10-01 09:53:52 +09:00
Rui Ueyama 49129fbd59 Keep SHF_LINK_ORDER sections alive with their linked sections
An SHF_LINK_ORDER section such as __patchable_function_entries holds
metadata for the section that its sh_link refers to, and usually
nothing else refers to it. --gc-sections therefore discarded all of
them except .ARM.exidx, for which there was an ARM-specific edge from
a section to its unwind index. Replace that edge with a generic one
from a section to the SHF_LINK_ORDER sections linked to it, as GNU ld
and lld do.
2026-10-01 09:53:52 +09:00
Rui Ueyama b2c3d1dc67 Ignore --gc-sections when creating a relocatable output
Allocated fragments of mergeable sections start out dead under
--gc-sections and are revived by the garbage collector, but mold -r
returns before running GC. As a result, `-r --gc-sections` discarded
every .rodata.str* and .rodata.cst* piece and left the relocations
referring to them with symbol index 0. Since mold doesn't collect
garbage for -r, ignore the option there. GNU ld and lld do collect
garbage with -r, taking roots only from -e and -u.
2026-10-01 09:53:52 +09:00
Rui Ueyama 6df3fd23bb Fix R_SPARC_WDISP16 encoding
R_SPARC_WDISP16 is used by branch-on-register instructions such as brz,
whose 16-bit word displacement is split into d16hi at bits 21:20 and
d16lo at bits 13:0. We ORed the fields into the upper halfword of the
instruction instead, corrupting its opcode, and limited the displacement
to 64 KiB instead of 128 KiB. Place the fields as GNU ld does.
2026-10-01 09:53:52 +09:00
Rui Ueyama 269fbb959c Keep R_SPARC_OLO10's second addend in -r output
R_SPARC_OLO10 carries a second addend in the upper 24 bits of r_info,
which is added after the low 10 bits of S + A are taken. RelocSection
built its output records with ElfRel::new(), which zeroes that field, so
`ld [%g1 + %lo(var) + 8]` lost its +8 in -r and --emit-relocs output.
Build each record from a copy of the input one instead.
2026-10-01 09:53:52 +09:00
Rui Ueyama 614e2a0326 Treat m68k GOT and TLS offsets as signed
The 8- and 16-bit GOT and TLS relocations fill the displacement of a
(d8,An,Xn) or (d16,An) operand, which the CPU sign-extends, but we checked
them against unsigned ranges. A GOT offset of 32 KiB or more was thus
silently encoded as a negative displacement, and a local-exec TLS offset,
which is usually negative because the thread pointer is biased by 0x7000,
was rejected. Check them as signed as GNU ld does.
2026-10-01 09:53:52 +09:00
Rui Ueyama 92161ddf08 Write SH4 .eh_frame addends in place for -r
SH4 object files keep relocation addends in the relocated places even
though the relocations are RELA, and RelocSection already follows that
convention. EhFrameRelocSection, however, put adjusted addends only into
r_addend, which SH4 readers ignore, so after `ld -r` every FDE's pc_begin
pointed at the start of its output .text and C++ exceptions terminated.
Write the addends into .eh_frame as we do for REL targets.

This enables the three -r exception tests that were skipped on SH4.
2026-10-01 09:53:52 +09:00
Rui Ueyama d9cf1f099e Preserve r2 in the SH4 lazy PLT header
On SH4, r2 carries the address of the buffer for a struct return value.
Our PLT header loaded the link map into r2 and set r0 to 0, which glibc's
resolver accepts, but the resolver then restores r2 to its entry value, so
a lazily bound function returning a struct overwrote the link map. The
header now passes the link map in r0 as GNU ld does, borrowing r2 only
until the jump's delay slot restores it from the stack.
2026-10-01 09:53:52 +09:00
Rui Ueyama 3dcd1df382 Refer to ICF leaders in emitted relocations
A section folded by ICF has no output section, so with --emit-relocs a
relocation against its section symbol was emitted with symbol 0, and one
in .rela.eh_frame, such as an LSDA pointer, hit an expect(). Symbols
defined in a folded section are not in .symtab either, so references to
them lost their symbol or, in .rela.eh_frame, got a wrong one. Emit both
relative to the leader's output section, as the relocated contents and lld
do.
2026-10-01 09:53:52 +09:00
Rui Ueyama fdf84222d8 Compute RISC-V TLSDESC IE offsets from the new AUIPC
When relaxing TLSDESC to initial-exec, we write an AUIPC to the
TLSDESC_ADD_LO12's slot and a load to the TLSDESC_CALL's slot, but we
computed their GOT-relative offsets from the TLSDESC_HI20's address. The
two coincide only if the instructions in between are deleted, which is not
the case for objects lacking R_RISCV_RELAX on TLSDESC relocations, such as
clang 18's, so the load read a wrong GOT entry. Use the AUIPC's address,
as lld does.
2026-10-01 09:53:52 +09:00
Rui Ueyama 7c8b80520a Rewrite relaxed TLSDESC_HI20 after pairing its LO12s
With --emit-relocs, a TLSDESC_HI20 relaxed to IE or LE was rewritten to
R_NONE as soon as it was applied. The TLSDESC_LOAD_LO12, TLSDESC_ADD_LO12
and TLSDESC_CALL that follow find it with find_paired_reloc(), which
matches by type, so such links failed with "paired relocation is missing".
Rewrite TLSDESC_HI20s after all relocations have been applied. LoongArch is
unaffected because its TLSDESC relocations refer to the symbol directly.
2026-10-01 09:53:52 +09:00
Rui Ueyama 52febe9fae Shift symbols in ICF-folded sections like their leader
shrink_sections adjusted each symbol by the r_deltas of its own input
section, but a section folded by ICF is never shrunk, so its symbols kept
their pre-relaxation offsets while being resolved against the shrunk
leader. With LoongArch GCC's leading R_LARCH_ALIGN nop, calls to a folded
function landed 4 bytes past the leader's entry. A folded section was
identical to its leader before relaxation, so use the leader's deltas for
both symbol values and st_size.
2026-10-01 09:53:52 +09:00
Rui Ueyama efda2fc149 Synthesize register save/restore functions for PPC64 ELFv1
GCC calls _savegpr0_N, _restgpr0_N, _savefpr_N and similar functions
from -Os prologues and epilogues and expects the linker to provide them.
We synthesized them only for ELFv2, so ELFv1 programs built with -Os
failed to link with undefined symbols. ELFv1 calls them with plain
R_PPC64_REL24 relocations, not through function descriptors, and
expects the same code, so emit the same section in target byte order
and move its instruction table from ppc64v2.rs to the section's module.
2026-10-01 09:53:52 +09:00
Rui Ueyama b34a3082e1 Compute PPC64 GOT16 relocations from the GOT slot address
R_PPC64_GOT16, _LO, _HI and _HA take the offset of a GOT slot from the
TOC base, but we subtracted the TOC base from the slot's offset within
.got rather than from its address, so the result was off by the address
of .got. GCC addresses data through .toc instead, so only hand-written
assembly using @got was affected.
2026-10-01 09:53:52 +09:00
Rui Ueyama acc8854e93 Check the range of PPC32 16-bit GOT offsets
With -fpic, PPC32 code loads a GOT slot with one instruction whose
R_PPC_GOT16 relocation is a signed 16-bit offset from the GOT, and
R_PPC_GOT_TLSGD16, R_PPC_GOT_TLSLD16 and R_PPC_GOT_TPREL16 are the
same. We truncated these offsets silently, so a program whose GOT grew
past 32 KiB linked but loaded the wrong slots. Report an error instead,
as the psABI requires overflow checks for these fields.
2026-10-01 09:53:52 +09:00
Rui Ueyama d3b2a74fa0 Include data references in the __rela_iplt range
A static executable's startup code applies the R_IRELATIVE relocations
between __rela_iplt_start and __rela_iplt_end, and we placed the start
by counting only the GOT slots of IFUNC symbols. On PPC64, a data
reference to an IFUNC symbol gets its own R_IRELATIVE instead of the
canonical PLT address, so the range began too late and skipped a GOT
slot, and a call through that slot ran the resolver. Count them too.
2026-10-01 09:53:52 +09:00
Rui Ueyama 80baa5dbac Make references to discarded COMDAT symbols fatal with --noinhibit-exec
A global symbol defined only in a member of a discarded COMDAT group has
no owning file, so it has nothing to resolve to. The relocation scan
reports a reference to it and skips the relocation, but --noinhibit-exec
downgraded the error to a warning and the relocation was applied anyway,
which panicked on a missing GOT or TLS entry on most targets. Report the
reference as a fatal error in that case.
2026-10-01 09:53:52 +09:00
Rui Ueyama 8f17166297 Process undefined-symbol relocations with --noinhibit-exec
The relocation scan skips a relocation to an undefined symbol when that
is an error, because the link fails anyway. --noinhibit-exec reports the
error as a warning and writes the output, so relocations that needed a
GOT, PLT or TLS entry were applied without one, and mold panicked on the
missing index. Such relocations are now scanned as they are with
--warn-unresolved-symbols, so the symbol gets the entries it needs and
resolves to 0, as it does in GNU ld and lld.

Every target crashed on a -fPIC object reading an undefined variable,
an undefined TLS variable, or both.
2026-09-30 23:23:08 +09:00
Rui Ueyama 8b9b014847 Write -r addends in place only on REL targets and SH4
GNU as emits R_RISCV_ALIGN and R_LARCH_ALIGN for an alignment directive
in an executable NOBITS section. With -r, RelocSection sliced the output
section's bytes at each relocation before calling write_addend, but a
NOBITS section has no bytes in the file, so the slice was out of range.
Since only REL targets and SH4 keep addends in section contents, take
the slice only on those targets.
2026-09-30 23:23:08 +09:00
Rui Ueyama 5b22233017 Apply R_RISCV_64 and R_LARCH_64 in 32-bit objects
GAS and Clang emit R_RISCV_64 and R_LARCH_64 for `.quad sym` even in
ELF32 objects. On RV32 and LA32, they are not the word-size absolute
relocation that the output section applies, so they reached
apply_reloc_alloc, which had no case for them and panicked. Handle them
like other non-word-size absolute relocations, as lld does: scan them
with scan_absrel and write S + A as a 64-bit value.
2026-09-30 23:23:08 +09:00
Rui Ueyama a368d2233d Restart for old LTO plugins from the original directory
With a plugin that lacks get_symbols_v3, mold re-executes itself with
the same command line. The new process applies -C again, but it was
started in the directory the first one had already changed to, so a
relative -C failed with "chdir failed". Remember the directory before
-C and return to it before re-executing.
2026-09-30 23:18:33 +09:00
Rui Ueyama 04e7dd8f70 Apply --chroot only once to paths derived from inputs
Input file names already have --chroot applied. Relative thin archive
members and relative names in linker scripts are resolved against such
a name, but the result was opened with --chroot applied again, so the
link failed. Open these paths as they are. Also write the --repro
archive next to the output in the chroot rather than outside of it.
2026-09-30 23:18:33 +09:00
Rui Ueyama e3bc3490b7 Archive --repro inputs from memory
--repro reopened each input by its recorded name. Response files and
--retain-symbols-file are opened before -C changes the directory, so a
relative name no longer found them and the link failed, and a name with
--chroot applied got the prefix twice. Archive the contents already in
memory instead: no input has been rewritten in place at this point.
2026-09-30 23:18:32 +09:00
Rui Ueyama d2b88ab395 Don't write the output in place over a mapped input
An existing output file is reused by writing it in place. When that file
is also a memory-mapped input, as in `mold a.o -o a.o` with an a.o larger
than 32 KiB, its private mapping sees the truncation and the writes while
the input is still being read, so mold crashed with SIGBUS or produced a
broken output. Such a file is now replaced with a fresh one. Inputs are
matched by device and inode, which covers archives and thin members too.
2026-09-30 23:18:32 +09:00
Rui Ueyama fdab1c33b2 Open absolute script paths as is without --sysroot
An absolute path in a linker script is prefixed with the sysroot if
the script is in the sysroot. is_in_sysroot returned false for an
empty sysroot, so such a path fell through to the -L search, and an
incompatible file was skipped with a warning. C++ mold treats an empty
sysroot as containing every file and opens the path as is, reporting
an error if it cannot be used. GNU ld and lld don't search -L either.
2026-09-30 23:13:53 +09:00
Rui Ueyama bc4272f76d Open dynamic lists before changing directory for -C
--dynamic-list and --export-dynamic-symbol-list files were opened by
the driver after parse_args had already changed the directory for -C,
so a relative path was looked up in the new directory. C++ mold reads
them in the option loop, as we already do for --retain-symbols-file.
Open them there too and keep the mapped files in DynamicListSource;
the driver still parses them, since the parser needs a Context.
2026-09-30 23:13:53 +09:00
Rui Ueyama a0d62d97f7 Remove the temporary output file on a panic
A panic aborts the process in release builds, skipping the cleanup that
a fatal error or a SIGSEGV/SIGBUS does, so a failed link left its
.<output>.<pid> file behind. Install a panic hook that removes the file
and then calls the default hook, so the panic message, backtrace and
abort stay as they were. The hook runs on the panicking thread, so it
also covers rayon workers and the extern "C" LTO plugin callbacks.
2026-09-30 23:13:53 +09:00
Rui Ueyama 3d7bed0abd Reject a NUL byte in a response file
Arguments are passed on as C strings, so a NUL byte in the -plugin path,
a -plugin-opt or the -o name made loading the LTO plugin panic. A
response file is the only way to get a NUL byte into an argument, as
the OS passes arguments as C strings too, so reject one when reading a
response file instead of at each C string conversion.
2026-09-30 23:13:53 +09:00
Rui Ueyama 9685b424d8 Link libatomic on hosts without 64-bit atomics
mimalloc uses 64-bit atomics, which GCC implements as calls into
libatomic on hosts such as 32-bit PowerPC. The mimalloc crate links
libatomic only on ARM, so mold failed to link there, which CI's cargo
check did not catch. Link libatomic when the target lacks 64-bit
atomics, as the C++ version did. It is a link argument rather than a
library because rustc would place the latter before mimalloc, where
--as-needed drops it.
2026-09-30 23:08:41 +09:00
Rui Ueyama c925e791ce Combine Adler-32 checksums in Rust
libz-sys declares the length parameter of adler32_combine() as
libc::off_t, which is 32 bits on 32-bit glibc hosts, but Debian and
Ubuntu now compile C code for armhf and powerpc with 64-bit off_t, so
the bundled zlib reads a 64-bit length there. Every zlib-compressed
debug section larger than one 1 MiB shard got a wrong checksum, and
readelf, objcopy and gdb rejected it. Compute the combination in Rust
instead; it is a few lines of modular arithmetic.
2026-09-30 23:08:41 +09:00
Rui Ueyama 1a694020fe Fix clippy warnings
Three of them came from a5511c1a7. CI doesn't run clippy.
2026-09-30 20:35:28 +09:00
Rui Ueyama fcd61c6e57 Don't read an instruction word at SPARC data relocations
apply_reloc_alloc read the 4-byte instruction at every relocation to
extract its register fields, so a 1- or 2-byte data relocation such as
R_SPARC_8 or R_SPARC_DISP16 in the last bytes of a section panicked.
Read the fields only for the relocations that rewrite an instruction.
2026-09-30 20:35:07 +09:00
Rui Ueyama f9828086ad Build on i686 hosts
i386 aligns 64-bit integers to 4 bytes, so Symbol and NameRecord are
smaller there than on other hosts, and the assertions that they are
exactly 48 and 16 bytes failed to compile. Assert upper bounds instead,
which still catch growth on 64-bit hosts, and check the i686 build in
CI.
2026-09-29 13:30:46 +09:00
Rui Ueyama 0ea3c18bc1 Build on hosts without 64-bit atomics
std has no AtomicU64 or AtomicI64 on hosts such as 32-bit PowerPC,
32-bit MIPS and pre-v6 ARM, so mold did not compile there, whereas the
C++ version fell back to libatomic. Take those two types from
portable-atomic, which uses the std types where they exist and falls
back to locks elsewhere. Also check the 32-bit PowerPC build in CI; the
existing armv7 check did not catch this because armv7 has 64-bit
atomics.

See https://github.com/rui314/mold/discussions/1643#discussioncomment-18603572.
2026-09-29 13:26:43 +09:00
Rui Ueyama a9c709b8a4 Merge pull request #1692 from bitemyapp/up/g23-deterministic-r-order
Order same-named output sections deterministically in -r output
2026-09-29 13:02:59 +09:00
Rui Ueyama 6b16418c6d Merge pull request #1691 from bitemyapp/up/g22-stale-comments
Remove stale comments and duplicated module docs
2026-09-29 12:59:19 +09:00
Rui Ueyama 04d6d800e0 Merge pull request #1690 from bitemyapp/up/g21-backend-endian
Use the shared endian helpers in the backends
2026-09-29 12:55:23 +09:00
Rui Ueyama 3e51fa1915 Merge pull request #1689 from bitemyapp/up/g19-test-runner
Give test scripts a closed stdin and return failure from run
2026-09-29 12:52:17 +09:00
Rui Ueyama ee6a2fa617 Merge pull request #1688 from bitemyapp/up/g18-link-entry-aliases
Name the link entry point's types once
2026-09-29 12:49:13 +09:00
Rui Ueyama 5d44b9f41d Restore the indexed loop in the glob NFA matcher 2026-09-29 12:45:05 +09:00
Rui Ueyama 427e9604ab Merge pull request #1687 from bitemyapp/up/g17-util-tidy
Tidy the utilities
2026-09-29 12:43:10 +09:00
Rui Ueyama 471142f238 Merge pull request #1686 from bitemyapp/up/g16-input-helpers
Remove duplicated helpers in the input layer
2026-09-29 12:28:17 +09:00
Rui Ueyama 8ea4678ba7 Revert two hunks from PR 1685
The .tbss layout loop gained a second end-of-list check next to the
existing one, for a case that cannot happen: .tbss is never the last
chunk. DigestMap::new was rewritten with repeat_with, which took five
lines to say what one line did. Restore both.

Follow-up to https://github.com/rui314/mold/pull/1685.
2026-09-29 12:00:33 +09:00
Rui Ueyama d947c52b3e Merge pull request #1685 from bitemyapp/up/g15-passes-loops
Simplify a few loops in the passes
2026-09-29 11:46:35 +09:00
Rui Ueyama 29d22ccdc7 Merge pull request #1684 from bitemyapp/up/g14-chunk-writers
Tidy the chunk writers
2026-09-29 11:34:03 +09:00
Rui Ueyama c1a509a3ff Drop FileRecord::size
FileRecord::size was size_of::<Self>(), and record sizes were written
both ways. Use size_of everywhere. Unlike the trait method, it is a
const fn, so it also works in the compile-time assertions.

https://github.com/rui314/mold/pull/1683 unified the spelling in the
other direction.
2026-09-29 11:06:35 +09:00
Rui Ueyama ee668f78a3 Use size_of and align_of from the prelude
Both have been in the prelude since Rust 1.80, so the std::mem:: prefix
is unnecessary.
2026-09-29 11:04:30 +09:00
Rui Ueyama 25d2a3ecf1 Report a failed link when SIGCHLD is ignored
An ignored SIGCHLD is inherited across exec, e.g. from `trap '' CHLD`
in a shell. The child that fork_child creates is then reaped
automatically, so the parent's waitpid fails with ECHILD. The parent
ignored that and read the untouched zero status as a successful exit,
so a link that failed exited with status 0. Report a failure instead.

Also read and write the completion byte through File, which removes
two raw libc calls.

Based on https://github.com/rui314/mold/pull/1682.
2026-09-28 23:09:54 +09:00
Rui Ueyama fd13e83077 Revert the from_hex and returns_etxtbsy changes from PR 1681
Both callers of from_hex already check that the byte is a hex digit,
so the new assertion was redundant. The utsname rewrite traded
MaybeUninit::assume_init for mem::zeroed without removing an unsafe
block, so restore the original code.
2026-09-28 22:27:50 +09:00
Rui Ueyama 9a4ad937d6 Merge pull request #1681 from bitemyapp/up/g11-cmdline-driver
Drop dead option arms and tighten two helpers
2026-09-28 20:14:08 +09:00
Rui Ueyama d17b876f6a Run abs-reloc-promotion.sh on targets other than x86-64
The fix it tests is target-independent, and only its last check was
x86-64-specific. Count the dynamic relocations against foo and bar
instead of looking for R_X86_64_64. PowerPC is skipped because GCC puts
the read-only pointers in writable sections there, so nothing is
promoted.
2026-09-28 20:04:42 +09:00
Rui Ueyama 3028b2c18c Accept a null symbol array from the LTO plugin
LLVMgold calls get_symbols with a null pointer and a zero count for an
IR file that has no symbols, e.g. one compiled from an empty source
file. slice::from_raw_parts does not accept a null pointer even for a
zero length, so the debug build aborted on its precondition check.
Use an empty slice for a zero count, in add_symbols as well.

Based on https://github.com/rui314/mold/pull/1680.
2026-09-28 20:01:17 +09:00
Chris AllenandClaude Opus 5.5 7f1b5647cc Report a thread pool failure as a fatal error
Building the thread pool fails when threads cannot be created, e.g.
under a low `ulimit -u`. That hit an expect, so mold aborted with a
panic message rather than reporting the error like other system
errors.

Taken from https://github.com/rui314/mold/pull/1678, which also
added checks for malformed inputs that are not included here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:48:41 +09:00
Rui Ueyama 339e870f18 Merge pull request #1679 from bitemyapp/up/g09-map-symbol-atomics
Use atomics for the concurrent map's key length and Symbol's CAS loops
2026-09-28 19:48:36 +09:00
Rui Ueyama 3ce0175c15 Merge pull request #1675 from bitemyapp/up/g04-abs-reloc-hardening
Make absolute relocation classification deterministic and check record alignment at compile time
2026-09-28 18:43:19 +09:00
Rui Ueyama 516456132e Make --dependency-file and --repro output deterministic
Input files are opened in parallel, and each file is appended to the
file pool when it is opened, so the pool's order changed from run to
run. --dependency-file and --repro list files in that order. Return
the pool sorted by name instead.

The dependency-file tests expected a space after a.o, i.e. that a.o
was not the last dependency. That failed intermittently on Alpine,
where libc.so is not a linker script and no dependency reliably comes
after a.o.
2026-09-28 18:40:02 +09:00
Chris AllenandClaude Opus 5.5 00b398ffd2 Pass the relocation record to check_range
report_out_of_range looked the record up again through the file's
relocation table while apply_reloc_alloc held that table mutably, which
created a shared view of memory behind a live unique reference, and it
walked the CREL iterator from the start to do so. Every caller has the
record at hand, so pass it in. The loop indices that only served the
lookup are gone with it.

Cherry-picked from https://github.com/rui314/mold/pull/1674.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:07:27 +09:00
Chris AllenandClaude Opus 5.5 4eb5f294b1 Look up CIE relocations through the file on each use
CieRecord stored a shared slice over its .eh_frame relocation records,
but reattach_fragment_relocations later rewrites those records in place
through the private input mapping, and the FDE and CIE accessors read
through the stale slice afterwards. The record now keeps only the
relocation section index and reads the current table through
ObjectFile::relocations, as decoded CREL tables already did. The raw
relocation view no longer claims a 'static lifetime.

Cherry-picked from https://github.com/rui314/mold/pull/1674.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:07:27 +09:00
Rui Ueyama e5a4e9351a Merge pull request #1672 from bitemyapp/up/g01-parity-section-order
Match C++ mold's program header and synthetic symbol order
2026-09-28 17:26:14 +09:00
Rui Ueyama cb2004af8d Update mold.1 (automated commit) 2026-09-28 07:52:39 +00:00
Rui Ueyama 830a8609aa Read single-dash long options as GNU ld and lld do
mold accepts a multi-letter option with a single dash, but the option
chain is first-match, and some single-letter options were tried before
long options starting with the same letter. "-entry=main" was thus read
as "-e ntry=main", "-filter libf.so" as "-f ilter" followed by an input
file, and "-fix-cortex-a53-843419" left a bogus DT_AUXILIARY entry. GNU
ld and lld read all of these as the long option.

Move each single-letter option past the last long option it shadows.
Conversely, GNU ld and lld read "-execute-only" as "-e xecute-only" and
"-omagic" as "-o magic", because those names take double dashes only.
Spell such names with double dashes in the option table, which
match_option now honors, replacing the rule that all names starting
with "o" need two dashes. Where GNU ld and lld disagree, follow GNU ld.

Thanks to @lu-zero for investigating this issue and proposing a fix in
https://github.com/rui314/mold/pull/1670.

Fixes https://github.com/rui314/mold/issues/1671
2026-09-28 16:49:46 +09:00
Rui Ueyama 6fc6e1916f Merge pull request #1673 from bitemyapp/up/g02-backend-reloc-fixes
Read CREL relocations of non-allocated sections on ARM32, PPC32, m68k and SH-4
2026-09-23 16:13:06 +09:00
Chris AllenandClaude Opus 5.5 13d2fa222c Tidy the utilities
Zip the glob NFA's three parallel arrays and fill a bracket range with
one slice fill, merge the two glob module headers left over from two C++
files, check SipHash's output width at compile time and split its input
into words with as_chunks, mark Timer must_use so a dropped one is a
compile-time warning, remove a Windows TODO from the C++ demangler that
never applied to this implementation, and fix a typo in a comment.

The merged glob header no longer lists qt_private_api_tag* as an
example of an Aho-Corasick pattern, since prefix patterns are matched
directly rather than by the Aho-Corasick matcher.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 23:29:26 -05:00
Chris AllenandClaude Opus 5.5 51e23d44d7 Promote symbols before classifying absolute relocations
Each output section promoted the imported symbols of its read-only
absolute relocations and classified its relocations in one pass, while
other sections ran in parallel. Whether a writable section's relocation
against the same symbol became a dynamic relocation therefore depended
on which section ran first, so the output changed from run to run. In a
non-PIE that references an imported variable and function from both
.rodata and .data, 37 of 100 links in one measurement emitted two extra
R_X86_64_64 dynamic relocations.

Collect all sections' relocations, promote, and only then classify, so
that the result is the same in every run: the word is resolved at link
time whenever a copy relocation or canonical PLT entry makes its address
known.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:16:38 -05:00
Chris AllenandClaude Opus 5.5 e874910ccb Simplify a few loops in the passes
The --undefined names were cloned only because get_symbol takes the
whole context mutably, so intern them through the symbol table instead.
The section-word reversal swaps whole words, the .tbss layout loop no
longer indexes past the last chunk, the has_lto_obj predicate replaces
an inline copy of itself, and the --exclude-libs ALL test is hoisted out
of the file loop. The dynamic symbol groups are extended in place, the
IRELATIVE count loses a temporary, and thunk addresses zip symbols with
their offsets.

In ICF, the digest no longer converts to little-endian on the way into
a hash whose key is random per run, the digest map is filled with
repeat_with, and HashMap is imported once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:16:01 -05:00
Chris AllenandClaude Opus 5.5 7ec87c99ce Read decoded relocations for non-allocated sections on all targets
apply_reloc_nonalloc on ARM32, PPC32, m68k and SH-4 read the relocation
section of a non-allocated section as an array of ordinary records. A
CREL table of a non-allocated section is not decoded ahead of time, so
for an object with SHT_CREL relocations those bytes are compressed
records: linking a PPC32 or m68k object that Clang 19 built with -g and
-Wa,--crel panicked with a failed length assertion in
records_from_bytes. Use the relocation iterator, which decodes CREL
tables, as the other targets already do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:15:45 -05:00
Chris AllenandClaude Opus 5.5 6d33903650 Order same-named output sections by their first member
A relocatable link keeps every COMDAT group's members in output
sections of their own, so an object with many groups of one name, such
as the .debug_macro groups GCC emits with -g3, produces output sections
whose sort keys tie. The tie fell back to the order in which the
parallel creation phase happened to make them, which changed from run
to run: 30 runs of the link in relocatable-compressed-debug-info.sh
produced 13 different outputs. Break the tie with the first member's
file priority and section index, which are in input order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:12:47 -05:00
Chris AllenandClaude Opus 5.5 dd3aaef2ef Add PPC64 save/restore symbols after __start_ and __stop_ symbols
C++ mold adds the _savegpr0_* family after the __start_ and __stop_
symbols, and the internal object's symbol order is the order in the
output .symtab. Match it so that ppc64le outputs get the same .symtab,
and hence the same build ID, as C++ mold's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:11:00 -05:00
Chris AllenandClaude Opus 5.5 a6c0d9de96 Emit PT_ARM_EXIDX and PT_RISCV_ATTRIBUTES after PT_GNU_RELRO
C++ mold defines these target-specific segments after PT_GNU_RELRO, but
the port defined them before PT_GNU_STACK. ARM and RISC-V outputs
therefore had their program headers in a different order than C++
mold's for the same input, and a different build ID. Define them where
C++ mold does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:11:00 -05:00
Chris AllenandClaude Opus 5.5 a7828b5986 Tidy the chunk writers
The four places that write a target word through an IS_64 branch
(.got, .got.plt, .relr.dyn and an output section's absolute
relocations) now use the existing Word::<E>::new(value).write(slot).
The padding filler walks its buffer with chunks_exact_mut; thunk
symbols zip their two parallel vectors; relr_offsets concatenates its
shards once; the .ARM.exidx lookup uses find_map instead of position
plus a second match, and text_end borrows the section headers instead
of copying them; .gnu.hash looks each symbol's hash up once;
merged-section resolution takes the timers by destructuring instead of
cloning them; OutputSectionId::new folds its two checks into one; the
.sframe writer slices each FRE block by start and length; WORD_SIZE is
derived from the word type like IS_64; and ElfDyn derives what its
siblings derive.

The doc comment on ChunkHeader's local-symbol fields no longer starts
with a stray "For --gdb-index", and a few comment typos are fixed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:07:38 -05:00
Chris AllenandClaude Opus 5.5 3f7da561ce Remove duplicated helpers in the input layer
shndx_at_in was identical to shndx_at, is_discarded_comdat re-implemented
is_discarded_comdat_sym, ObjectFile's Display duplicated display_file,
rels_mut looked its table up twice, find_null re-implemented a chunked
position search, the archive member name reader re-implemented cstr_at
and memchr, the file type probe indexed the header table it was
iterating, and the linker script reader spelled slice patterns as length
checks plus indexing.

The loop in is_dwarf32 now compares the end of the next unit header with
the section size instead of subtracting its start from the size, which
wrapped around when a unit's size field pointed past the end of a
malformed section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:06:36 -05:00
Chris AllenandClaude Opus 5.5 50e0643cb1 Name the link entry point's types once
The command-line and result types of the per-target link functions were
spelled out in the twenty target crates, the executable and the driver.
Name them Cmdline and LinkResult in the driver and use the names
everywhere else.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:06:20 -05:00
Chris AllenandClaude Opus 5.5 9b50d69058 Check record alignment at compile time
The record views in elf.rs relied on a debug assertion, and therefore on
the FileRecord contract alone in release builds, for the alignment that
their pointer casts require. An inline const assertion is evaluated per
instantiation, so a record type that violates the contract no longer
compiles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:06:04 -05:00
Chris AllenandClaude Opus 5.5 b4bed3505e Remove stale comments and duplicated module docs
Several comments compared the code to C++ mold's arena storage and
chunks, or named functions that do not exist in this implementation
(SharedFile<E>::mark_live_objects, Symbol<E>::get_addr and
open_file_impl); point them at the Rust functions or drop the
comparison. The ICF and thunks modules repeated their description in a
second module-level summary, and the TLS module docs had a stray title
line. drop_mappings did not say that it discards the relocation records
rewritten in place, and the madvise in write_build_id did not say why
dropping written pages of the output loses nothing. Also fix three
typos.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:04:32 -05:00
Chris AllenandClaude Opus 5.5 3bfc9b6d97 Keep pointer provenance in madvise_hugepage_interior
The page-aligned start of the interior range was turned back into a
pointer with an integer cast, which leaves it without the allocation's
provenance. Derive it from the caller's pointer with with_addr instead,
as the rest of the function already reads addresses with addr().

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:03:01 -05:00
Chris AllenandClaude Opus 5.5 1261269fd8 Use std's atomic helpers in Symbol
record_comdat_owner and set_visibility_bits hand-rolled fetch_min and
update. record_comdat_owner keeps its initial load so that a file that
does not lower the priority still leaves the cache line shared. The
packed visibility and flags bytes are no longer pub, since nothing
outside symbol.rs touches them and a direct store would corrupt the
state bits they share.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:02:42 -05:00
Chris AllenandClaude Opus 5.5 377abf91e0 Use the shared endian helpers in the backends
x86-64 and i386 defined their own little-endian write helpers with the
same bodies as write_ul16, write_ul32 and write_ul64 in util::endian,
and i386 read REL addends with inline from_le_bytes calls. Call the
shared functions directly, as the other fixed-endian targets do. The
ARM64 and RISC-V instruction accessors now delegate to them, like
LoongArch's already do.

Also drop an unused parameter of relax_tlsdesc_to_ie, a redundant mask
on SPARC relocation types (the record keeps the type in a byte of its
own), two TRAP comments that described C++ target structs instead of
the instruction, and the pub on two PPC64 ELFv1 functions that only
their module uses. SH-4 reports an unknown relocation with error! like
every other target, so that all of them are listed in one run.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:01:34 -05:00
Chris AllenandClaude Opus 5.5 48cae3a63a Make the concurrent map's key length an atomic
The key length was an UnsafeCell whose reads and write needed unsafe
blocks justified by the release/acquire pair of the key pointer. A
relaxed atomic expresses the same protocol, compiles to the same loads
and stores, and takes the probe comparisons out of unsafe code. Also
check the probe invariant at compile time and reject keys longer than
32 bits instead of truncating their lengths.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 22:00:47 -05:00
Chris AllenandClaude Opus 5.5 4348731de1 Tighten two helpers
driver is a public module, so `pub` made copy_chunks part of the
library's API although only the crate itself calls it; make it
pub(crate). from_hex now asserts its precondition in debug builds
instead of silently producing garbage for a non-hex byte.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 21:57:25 -05:00
Chris AllenandClaude Opus 5.5 8335147fe2 Drop dead option arms and simplify the uname call
Six flags in the ignored-for-compatibility chain were unreachable
because earlier arms already consume them: color-diagnostics,
eh-frame-hdr, fatal-warnings, enable-new-dtags, disable-new-dtags and
warn-once.

returns_etxtbsy zeroed a MaybeUninit<utsname> and then asserted it
initialized after calling uname. utsname consists of byte arrays, so a
zeroed one is already a valid value; start from that and drop the
assume_init step.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 21:57:24 -05:00
Chris AllenandClaude Opus 5.5 61fa2e17ca Give test scripts a closed stdin and return failure from run
The runner starts each script in its own process group, so a script
that read the terminal would stop there until the timeout. Give it
end-of-file instead.

run already returns ExitCode, so report setup errors through it
instead of exiting the process. Also drop the second sort of the
discovered scripts (make_jobs sorts the combined list), the unused
Clone on TestJob and the intermediate vector in the PATH search, and
give the unsafe kill a SAFETY comment like the other unsafe blocks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-22 21:56:27 -05:00
Rui Ueyama 99d79c429b Don't allocate a GOT entry for GOT-base relocations
R_X86_64_GOTPC32, R_386_GOTPC, R_390_GOTPC and their variants compute
GOT + A - P, the distance to the start of the GOT section rather than
to an entry in it. They were grouped with the GOT-indirect relocations,
so every link that referenced the GOT base got an entry for
_GLOBAL_OFFSET_TABLE_ that nothing ever read, plus a RELATIVE
relocation to fill it in a PIE.
2026-09-21 16:00:33 +09:00
Rui Ueyama 7fe68bf109 Scan R_ARM_REL32 like other PC-relative relocations
R_ARM_REL32 was in the bucket of relocations that need no scanning,
so a reference to a symbol defined in a DSO was silently resolved as
if the symbol were at address zero. Route it through scan_pcrel like
PREL31 and MOVT_PREL, which gives imported data a copy relocation,
an imported function a canonical PLT entry, and a shared object an
error.
2026-09-21 15:57:25 +09:00
Rui Ueyama 659780e6c4 Scan GOT-relative relocations like PC-relative ones
R_X86_64_GOTOFF64 and its counterparts on i386, ARM32, s390x and SH4
compute S + A - GOT, which is a link-time constant only if the symbol
is in the output file. We didn't scan them at all, so a reference to a
symbol defined in a DSO was silently resolved as if the symbol were at
address zero. Route them through scan_pcrel, which already makes the
right decision: a copy relocation for imported data, a canonical PLT
entry for an imported function, and an error in a shared object.

Fixes https://github.com/rui314/mold/issues/1668
2026-09-21 15:57:25 +09:00
Rui Ueyama 254ccb339c Make is_absrel a Target method and skip those relocations everywhere 2026-09-21 15:20:19 +09:00
Rui Ueyama a5511c1a7b Replace pointer-and-assertion patterns with bounds-checked views
The port kept C++ idioms in which a raw pointer or an unchecked index
stood in for a slice, guarded by an assertion. Add SyncUnsafeCell, a
slice-of-cells view for parallel passes, and use it for the file
mapping, the symbol-table allocators, the symbol editor and decoded CREL
tables. Make the fields that parallel passes write atomic, which turns
allocate_aux, the dynsym pass, the gdb_index prefix scan and the COMDAT
signature slots into safe code. Write merged sections and the gdb_index
tables through split slices, and look up files and sections with plain
bounds checks.

This removes 58 unsafe blocks and 16 assertions.
2026-09-21 15:20:19 +09:00
Rui Ueyama 2d4a214d6e Merge pull request #1669 from nmoinvaz/perf/local-tests-first
Check a file's own symbol entry before looking the symbol up
2026-09-21 14:24:14 +09:00
Nathan Moin Vaziri c73791304f Check a file's own symbol entry before looking the symbol up
check_duplicate_symbols and check_symbol_types looked every reference
up in the symbol table, a cache miss each in a large link, before checks
on the file's own entry that skip it anyway. An undefined reference is
never a duplicate, and one without a type never mismatches.

Assisted-By: Claude <[email protected]>
2026-09-20 20:37:46 -07:00
Rui Ueyama 89a9b334e8 Merge pull request #1665 from nmoinvaz/perf/origin-tag-tests 2026-09-21 10:59:28 +09:00
Rui Ueyama c19c87988f Merge pull request #1664 from nmoinvaz/perf/parallel-got-dynrel-count 2026-09-21 10:59:06 +09:00
Rui Ueyama 15459ac587 Merge pull request #1666 from artemkulyk/aarch64-tlsle-ldst 2026-09-21 09:53:35 +09:00
Rui Ueyama 8afbeb85e3 Merge pull request #1663 from nmoinvaz/perf/cached-symbol-type
Check symbol types without loading the owner's symbol entry
2026-09-21 09:21:08 +09:00
Rui Ueyama c2e3a64db0 Merge pull request #1662 from nmoinvaz/perf/hoist-loop-invariant-reloc-reads
Read the loop-invariant relocation addresses once
2026-09-21 08:38:56 +09:00
artemkulyk a4b64d86b4 Fix AArch64 TLSLE LDST relocations
R_AARCH64_TLSLE_LDST{8,16,32,64,128}_TPREL_LO12_NC were unhandled, so
objects using them failed to link with "unknown relocation".  The immediate
is the local-exec TLS offset scaled by the access size.
2026-09-20 20:56:35 +02:00
Nathan Moin Vaziri 94adbf6e7f Test the origin's tag bits when taking a symbol's address
addr_with decoded the tagged origin word into its five-variant enum up
front and matched it twice, which left the enum spilled to the stack on
every relocation. It only needs to know whether the origin is a fragment
or an input section, and each is a mask and a compare.

Assisted-By: Claude <[email protected]>
2026-09-20 10:19:07 -07:00
Nathan Moin Vaziri 2777a8229e Check symbol types without loading the owner's symbol entry
The check read every referenced symbol's ELF entry from its owning file,
a second cache miss per reference, only to get the type the symbol
already caches.

Assisted-By: Claude <[email protected]>
2026-09-20 09:54:43 -07:00
Nathan Moin Vaziri 2157032638 Count the GOT's dynamic relocations in parallel
Sizing .rela.dyn walks every GOT symbol, a cache miss each, and the
section is sized three times on the way to the final layout. The walk
was the only serial one of that size left before the copy.

Assisted-By: Claude <[email protected]>
2026-09-20 09:50:47 -07:00
Nathan Moin Vaziri cfcd3137df Skip x86-64 absolute relocations the output section applies
scan_abs_relocations already collects every R_X86_64_64 in an allocated
section, and apply_abs_rels applies it. Resolving the symbol again here
only to fall through an empty match arm is wasted work.

Assisted-By: Claude <[email protected]>
2026-09-20 08:45:40 -07:00
Nathan Moin Vaziri 135b1e1a06 Compute the x86-64 GOT displacement only where it is used
Six of the fifty relocation kinds need G, but every relocation paid for
its aux-table lookup and bounds check. Move the computation into a
closure, as the other targets already do.

Assisted-By: Claude <[email protected]>
2026-09-20 08:44:28 -07:00
Nathan Moin Vaziri 244154313a Read the loop-invariant relocation addresses once
A section's address and the GOT base do not change while its relocations
are applied, but the section offset is atomic and the context carries
interior mutability, so the compiler must reload both after every store
into the output buffer. Read them before the loop instead.

Assisted-By: Claude <[email protected]>
2026-09-20 08:39:48 -07:00
Rui Ueyama c6a2cbc577 Fix CI 2026-09-20 18:46:58 +09:00
Rui Ueyama a9800541d0 Keep a compressed section's contents empty until it is uncompressed
InputSection stores its contents as a pointer and sh_size. For a
compressed section, sh_size is the Chdr's uncompressed size while the
pointer led to the compressed bytes, so reading contents() before
uncompress() made a slice far longer than its buffer. Only a debug_assert
guarded this, and a crafted SHF_COMPRESSED .eh_frame reached it through
the .eh_frame parser.

Store a null pointer for compressed and NOBITS sections instead, so that
a nonnull pointer always has sh_size bytes behind it, and pass the
compressed bytes to uncompress() from the file's section header. The gABI
forbids compressing allocated sections, so reject them with an error.
2026-09-20 17:13:43 +09:00
Rui Ueyama a31c187dbb Write output sections through split slices instead of raw pointers
Parallel writes into an output section went through OutputBuffer, a raw
pointer whose ranges were computed by the caller and only debug-asserted.
For ARM32 BE8 byte swapping those ranges came from mapping-symbol values
in the input file, so a corrupted symbol wrote outside of its section.

Replace OutputBuffer with for_each_member, which splits the buffer at
member offsets with rayon::iter::split and split_at_mut and hands each
member its own slice. Thunks get their slices by splitting the buffer
after the member loop, and the ARM32 swap indexes mapping-symbol ranges
within the member's slice. No unsafe code remains in either file.
2026-09-20 16:57:20 +09:00
Rui Ueyama 20bbba9101 Apply absolute relocations through the member's own slice
Word-size absolute relocations were applied after the copy pass through
a raw pointer into the output section, with only a debug_assert on the
offset. A corrupted r_offset therefore wrote past the section in release
builds instead of failing.

Apply them in the per-member copy loop instead, indexing the member's
slice so that an out-of-range r_offset panics. abs_rels is built in
member order, so each member's relocations are one run of the vector;
AbsRel now records the member index and write_to finds the run with
partition_point. This removes write_word and its SAFETY assumption that
input relocations do not overlap.
2026-09-20 15:52:02 +09:00
Rui Ueyama 7f26bc1097 Format 2026-09-20 12:12:28 +09:00
Rui Ueyama d567d10f15 Raise the default test timeout to 60 seconds
The 80k-section tests take about 2 seconds alone but 13 to 14 seconds
on every target when the suite runs 128 jobs at once, so any other load
on the machine pushed some of them past 30 seconds. The limit exists to
catch hangs, and 60 seconds still does that promptly.
2026-09-20 11:44:13 +09:00
Rui Ueyama 43a004eab6 Fold the Layout trait into Target
Layout carried only the file format: byte order, word size and the
record forms that differ between ELF32 and ELF64. No type implemented it
without also implementing Target, so the split earned its keep only in
RISC-V and LoongArch, where the record types depend on a const generic
and could not be chosen inside one Target impl.

Those two targets now take their record types from Class<IS_64>, a
helper private to the target module that maps the const parameter to
the ELF32 or ELF64 forms. ARM32 and SH4 lose their per-byte-order impls
and read section header words through u64::from like the other 32-bit
targets.
2026-09-20 11:44:08 +09:00
Rui Ueyama 4afe0ffd5b Rename the Arch trait and module to Target
What the trait enumerates are targets, not architectures: Ppc64V1 and
Ppc64V2 share an architecture and differ in ABI, and Arm64 and Arm64Be
differ only in byte order. The architecture-level notion already has its
own name, Family. The rest of the tree already said target, in
Arm64Target, TargetTraits, emulation_to_target and the comments, so the
trait was the one place the vocabulary broke. The workspace directory
arch/ keeps its name because target/ at the repository root is Cargo's
build directory.
2026-09-20 11:44:08 +09:00
Rui Ueyama 0e9fa3df08 Make Layout the only type parameter of ELF integers and records 2026-09-19 23:53:23 +09:00
Rui Ueyama 444cfcf971 Drop the Word item from the record traits 2026-09-19 23:53:23 +09:00
Rui Ueyama 0b3094f73a Spell a target's record types through the aliases everywhere 2026-09-19 23:53:23 +09:00
Rui Ueyama 2e6a67f188 Store r_info as one word in relocation records 2026-09-19 23:53:23 +09:00
Rui Ueyama 5331eafc08 Make ELF record accessors take and return host integers 2026-09-19 23:53:23 +09:00
Rui Ueyama 0ab442dbc6 Stop using grep -q in test pipelines
common.inc sets pipefail, and grep -q exits as soon as it finds a match.
If the producer still has output to write, it dies of SIGPIPE, the
pipeline fails, and a test that gates on the match silently skips or
fails. Plain grep reads its whole input, so the pipeline's status is
grep's own.
2026-09-19 23:53:23 +09:00
Rui Ueyama 60b97830ca Merge pull request #1660 from noritada/as_chunks
Use `<[T]>::as_chunks` instead of `<[T]>::chunks_exact`
2026-09-19 23:53:05 +09:00
Noritada Kobayashi d83916655c Use <[T]>::as_chunks instead of <[T]>::chunks_exact
This makes it clear, in terms of type, that each chunk is an array of
fixed length (`&[u8; 4]`), rather than a slice of variable length
(`&[u8]`).
2026-09-19 19:29:32 +09:00
Rui Ueyama 54b68f6e42 Merge no-op match arms that clippy flagged as identical
Clippy's match_same_arms lint reports 15 pairs. Most sit in relocation
tables that follow the relocation number order, where merging would
break the order, so only the two that are not in such tables change.
2026-09-19 14:07:08 +09:00
Rui Ueyama 36ec82df73 Drop pub(crate) on items in private modules
Apply clippy's redundant_pub_crate suggestion. An item in a private
module is crate-visible at most, so `pub(crate)` on it says nothing
that `pub` does not.
2026-09-19 14:07:08 +09:00
Rui Ueyama c5f69d9181 Use pointer casts and raw borrows instead of as casts
Apply clippy's ptr_as_ptr, borrow_as_ptr, ref_as_ptr and
ptr_cast_constness suggestions. `ptr.cast()` and `ptr::from_ref()`
cannot silently change the pointee type when a declaration elsewhere
changes, and `&raw mut x` passes a pointer to C without creating an
intermediate reference.
2026-09-19 13:42:51 +09:00
Rui Ueyama b56daa27b7 Merge pull request #1659 from adamnemecek/main
Use `Self` when possible.
2026-09-19 13:20:05 +09:00
adamnemecek 3bf748a8fd a couple more Self's 2026-09-18 19:57:25 -07:00
adamnemecek 3ca1cb6579 cargo fmt 2026-09-18 19:52:42 -07:00
adamnemecek 733952657a ran cargo clippy --allow-dirty --fix -- -Wclippy::use_self 2026-09-18 19:52:34 -07:00
Rui Ueyama 6fad4d5cf1 Fix CI 2026-09-19 10:49:28 +09:00
Rui Ueyama 2aeddd8659 Update .gitignore 2026-09-19 09:49:37 +09:00
Rui Ueyama 722d8f02bc Drop unneeded Cargo settings 2026-09-19 09:45:07 +09:00
Rui Ueyama 75a935d071 Simplify 2026-09-18 22:24:20 +09:00
Rui Ueyama 0fec5eec58 Report out-of-range non-allocated CREL relocations 2026-09-18 21:46:23 +09:00
Rui Ueyama f615c28339 Keep --package-metadata as bytes 2026-09-18 21:46:22 +09:00
Rui Ueyama e41ed90846 Drop COMDAT groups without members from -r output 2026-09-18 21:46:17 +09:00
Rui Ueyama ec2c9fcbc2 Apply RISC-V non-allocated relocations from CREL tables 2026-09-18 21:46:15 +09:00
Rui Ueyama 1b17bdf0d2 Write back the addends of all relocations that -r passes through 2026-09-18 21:46:12 +09:00
Rui Ueyama 6cd991a3e4 Bind PPC64 ELFv1 .opd references to the first symbol of a descriptor
rewrite_opd looks up the symbol of a function descriptor by its offset
with a binary search. Aliases share a descriptor, so the search could
land on any of them, and a section-relative reference to a static
function then went through one of its global or weak aliases: it became
preemptible and resolved to whichever definition won. Use the lower
bound instead; the stable sort keeps the local symbol first.
2026-09-18 21:46:12 +09:00
Rui Ueyama 8dfbccd8a6 Keep local symbols of non-allocated mergeable sections in .symtab 2026-09-18 21:46:01 +09:00
Rui Ueyama ec3a8d247b Fix --wrap for symbols whose names start with __real_ 2026-09-18 21:45:54 +09:00
Rui Ueyama ebbda8f4bc Collapse nested ifs into let chains 2026-09-18 16:58:23 +09:00
Rui Ueyama 61c0854ddc Reformat for rustfmt's 2024 style edition 2026-09-18 16:58:21 +09:00
Rui Ueyama 1efb465bfa Move to Rust edition 2024 2026-09-18 16:58:18 +09:00
Rui Ueyama fa30ac90cb Add README files for src/arch and src/chunks 2026-09-18 15:04:37 +09:00
Rui Ueyama 9b9d9d7ef9 Adopt rustfmt's Max width heuristics 2026-09-18 14:19:38 +09:00
Rui Ueyama 8ceae6875f Fix CI 2026-09-18 13:45:24 +09:00
Rui Ueyama e61c7dbcb7 Update README 2026-09-18 13:11:25 +09:00
Rui Ueyama d64a0ce248 Restore GitHub Actions after Rust migration 2026-09-18 12:25:09 +09:00
Rui Ueyama a7e9aac9d3 Remove reports 2026-09-18 12:25:09 +09:00
Rui Ueyama af97c80062 Merge Rust implementation and its history 2026-09-18 10:07:47 +09:00
Rui UeyamaandClaude Fable 5.1 d659f5930f Vendor Cargo dependencies for the distribution build
`dist.sh arm` failed in the networked fetch step: cargo could not read
its git database for the mimalloc dependency ("Value too large for
defined data type"). ext4 hands 64-bit directory offsets to the QEMU
process, the armhf glibc reports EOVERFLOW when they don't fit a 32-bit
off_t, and the libgit2 built into the 32-bit cargo uses that interface.
Only the git dependency is affected; everything else goes through Rust's
std, which uses the 64-bit calls.

Keep cargo's cache on a tmpfs inside the networked container and have it
`cargo vendor --locked` into target/dist-vendor-$arch together with the
printed config. The offline build reads the vendored sources through
`--config` and no longer needs a cargo home at all.

Tests: ./dist.sh x86_64

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0141irCwtPmPMZa9s7pCN3Cz
2026-09-17 22:26:49 +09:00
Rui UeyamaandClaude Fable 5.1 011e194896 Build on 32-bit hosts
Two struct-size assertions hold only with 8-byte pointers: SymbolAux ends
in a Vec and PendingComdatSignature starts with a fat pointer, so they
are 48 and 16 bytes on a 32-bit host and rustc rejected the crate for
armv7. Gate them on target_pointer_width = "64"; the other five size
assertions have no pointer-sized fields and pass on both widths.

CI now runs cargo check for armv7-unknown-linux-gnueabihf over the whole
workspace so this cannot regress unnoticed.

Tests: cargo check --release --target armv7-unknown-linux-gnueabihf --workspace --all-targets
Tests: cargo test --release (pass=5681 skip=654 fail=0)

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0141irCwtPmPMZa9s7pCN3Cz
2026-09-17 22:26:49 +09:00
Rui UeyamaandClaude Fable 5.1 8fe0ffb586 Pin apt sources to snapshot.debian.org for every Debian image
The bullseye targets (aarch64, arm, ppc64le, s390x) left apt on the live
deb.debian.org mirrors. Those mirrors dropped the architectures Debian LTS
does not support when bullseye entered LTS, so `dist.sh ppc64le` and
`dist.sh s390x` fail in `apt-get install` with "Unable to locate package
build-essential". The pinned snapshot the images ship commented out in
sources.list still has them, and using it also gives these builds the
reproducibility the header comment already claims.

Make the snapshot switch the default apt_setup; riscv64 (deb822 sources)
and loongarch64 (live sid) keep their own.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0141irCwtPmPMZa9s7pCN3Cz
2026-09-17 17:37:49 +09:00
Rui Ueyama 8794986da3 Fix sed portability issue in test 2026-09-16 12:56:07 +09:00
Rui Ueyama 44bfa2a9b2 Preserve DSO symbol versions during archive extraction
Retain the original versioned reference when following undefined symbols
in shared libraries. This prevents an unversioned archive definition from
causing unnecessary extraction while preserving symbol interposition.
Skip the retained undefined references in the secondary symbol type check
and port both upstream regression tests unchanged.

Validation: full workspace tests passed with 34 unit tests and 5,684
integration tests across 14 target configurations; 651 skipped, 0 failed.
Four additional targets lacked a compiler or QEMU. The archive regression
fails before the fix, and both new tests pass on all available targets.

Upstream-Commit: 294e22a86a
2026-09-16 11:58:29 +09:00
Rui Ueyama 294e22a86a Preserve DSO symbol versions during archive extraction
Retain the original versioned reference when following undefined
symbols in shared li6braries. This prevents an unversioned archive
definition from causing unnecessary extraction while preserving symbol
interposition.

Fixes https://github.com/rui314/mold/issues/1657
2026-09-16 11:45:53 +09:00
Rui Ueyama 60f9b36dcf Generate large LTO section tables without compiling thousands of functions
Append 70,000 empty sections to GCC-generated LTO assembly for a tiny
program. Preserve the extended section numbering checks for slim and
fat LTO objects and verify that the fat object actually invokes LTO.

Validation: this test passes on all 14 available target configurations.
The observed LoongArch run dropped from 19 seconds to 4.4 seconds; the
14-target run completed in 5.7 seconds.
2026-09-15 21:34:37 +09:00
Rui Ueyama b24a27a9f2 Check merged debug strings in one awk scan
Replace 1,000 grep invocations with one awk process while checking all
500 DWARF32 and 500 DWARF64 names. This avoids timeouts spent repeatedly
spawning grep during parallel test runs.

Validation: the updated test passes on all 14 available target
configurations. The check accepts a complete dump and rejects missing
names and empty input.
2026-09-15 20:39:29 +09:00
Rui Ueyama b56649e972 Omit .gnu_debuglink from separate debug files
Remove the NOBITS debug-link placeholder and remap section indices so
symbols and section headers still refer to the correct sections. Port
the upstream regression test unchanged.

Validation: separate-debug-file tests across 14 target configurations:
29 passed, 13 skipped, 0 failed. The regression fails before the fix and
passes afterward.

Upstream-Commit: f4edcbd37e
2026-09-15 19:52:45 +09:00
Rui Ueyama f4edcbd37e Omit .gnu_debuglink from separate debug files
Remove the malformed NOBITS debug link and adjust section indices so
symbols and section headers still refer to the correct sections.

Add a regression test for following the debug link and preserving
symbols in later sections.

Fixes https://github.com/rui314/mold/issues/1656
2026-09-15 16:52:04 +09:00
Rui UeyamaandClaude Fable 5.1 1f6b4b4c32 Emit .eh_frame relocations in output order for -r
The relocatable output lays out all leader CIEs before the FDE blocks
of every file, but EhFrameRelocSection::copy_buf emitted the
relocations per file: a file's CIE relocations followed by its FDE
relocations. When a file after the first has a CIE with a relocation,
which is any object with a personality routine, that relocation was
written after the previous files' FDE relocations, so .rela.eh_frame
was not sorted by offset.

Consumers pair CIEs and FDEs with a contiguous run of relocations in a
single scan over .eh_frame, and parse_ehframe is one of them. On the
unsorted section the personality relocation was attached to an earlier
file's FDE with a negative in-record offset, so the final link wrote the
value into the preceding FDE and left the CIE's personality pointer
zero. libgcc treats an encoded zero as "no personality", so functions
under that CIE lost their cleanups and catch handlers: an exception
thrown inside their try block escaped uncaught. Found by dev-util/perf,
whose build partially links a Rust workload object after C objects.

Emit the CIE relocations of every file first and only then the FDE
relocations, which matches the layout, and add a test that links a C
object before a C++ object with `-r`, checks that the relocations are
sorted, and catches an exception through the result.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0141irCwtPmPMZa9s7pCN3Cz
2026-09-15 16:52:04 +09:00
Rui UeyamaandClaude Fable 5.1 0e41bb98e4 Emit .eh_frame relocations in output order for -r
The relocatable output lays out all leader CIEs before the FDE blocks
of every file, but the relocations for them were emitted per file: a
file's CIE relocations followed by its FDE relocations. When a file
after the first has a CIE with a relocation, which is any object with
a personality routine, that relocation was written after the previous
files' FDE relocations, so .rela.eh_frame was not sorted by offset.

Consumers pair CIEs and FDEs with a contiguous run of relocations in a
single scan over .eh_frame, mold included. On the unsorted section the
personality relocation was attached to an earlier file's FDE with a
negative in-record offset: we panicked on the slice index, and C++ mold
silently wrote the value into the preceding FDE and left the personality
pointer zero, which libgcc treats as "no personality", dropping that
function's cleanups and catch handlers. Found by dev-util/perf in the
Gentoo campaign; a Rust workload object partially linked after C objects
triggered it.

Emit the CIE relocations of every file first and only then the FDE
relocations, which matches the layout. The C++ implementation received
the same change.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0141irCwtPmPMZa9s7pCN3Cz
2026-09-15 11:13:36 +09:00
Rui Ueyama c52c765f98 Document simplification commits and correctness validation 2026-09-15 01:27:29 +09:00
Rui Ueyama 3ccb8e2291 Preserve lazy addend reads when emitting relocations 2026-09-15 01:05:16 +09:00
Rui Ueyama 8a56434bae Limit implementation module visibility and remove stale explanations 2026-09-15 00:57:51 +09:00
Rui Ueyama 4658ec473a Use native zlib for both compression and decompression 2026-09-15 00:56:16 +09:00
Rui Ueyama b2effe0931 Use bounded memchr searches for NUL-terminated strings 2026-09-15 00:55:29 +09:00
Rui Ueyama e918027933 Use bounded static chunks for GDB background work 2026-09-15 00:54:40 +09:00
Rui Ueyama ebc789e41f Use existing map entry IDs for interned GDB names 2026-09-15 00:54:07 +09:00
Rui Ueyama 46bbf3df03 Gather owned sparse output-section contributions per file 2026-09-15 00:52:47 +09:00
Rui Ueyama 19162bea6c Use the standard indexed parallel iterator for live files 2026-09-15 00:51:53 +09:00
Rui Ueyama c77f241e9b Deduplicate CIEs with file and record indices 2026-09-15 00:50:58 +09:00
Rui Ueyama bb257d5a37 Unify test process errors and result finalization 2026-09-15 00:49:51 +09:00
Rui Ueyama 67ba746692 Centralize GNU option parsing and declarative option forwarding 2026-09-15 00:47:39 +09:00
Rui Ueyama 284c571636 Share output symbol and addend mapping for emitted relocations 2026-09-15 00:44:20 +09:00
Rui Ueyama 1c639473ab Share nonallocated relocation resolution across architectures 2026-09-15 00:42:28 +09:00
Rui Ueyama 8e0967d72b Stream dependency reports through the shared buffered writer 2026-09-15 00:40:08 +09:00
Rui Ueyama 9d9f0e96e7 Compute final statistics without global atomic counters 2026-09-15 00:39:27 +09:00
Rui Ueyama 65479de34d Keep output files with their mapping state 2026-09-15 00:38:42 +09:00
Rui Ueyama 9bca3eacef Traverse archive members lazily through one iterator 2026-09-15 00:36:38 +09:00
Rui Ueyama 15b984a27c Assign input file IDs after sorting owned reader results 2026-09-15 00:34:45 +09:00
Rui Ueyama d6039a2a58 Encapsulate cooperative background jobs 2026-09-15 00:33:48 +09:00
Rui Ueyama 4980d26ad8 Share persistent worker bins and caches 2026-09-15 00:32:54 +09:00
Rui Ueyama d677902cc0 Declare chunk header lookup once for shared and mutable access 2026-09-15 00:31:37 +09:00
Rui Ueyama 95fef65b28 Represent header-only output sections directly as chunk headers 2026-09-15 00:30:01 +09:00
Rui Ueyama 2712c5e183 Share 32-bit and 64-bit ELF record accessors 2026-09-15 00:28:42 +09:00
Rui Ueyama 7d6d588ee3 Derive page size and glob emptiness from existing state 2026-09-15 00:27:40 +09:00
Rui Ueyama de4910ff57 Keep parser-only settings out of Args 2026-09-15 00:27:40 +09:00
Rui Ueyama 2308083712 Remove unused symbol and endian helpers 2026-09-15 00:26:50 +09:00
Rui Ueyama f7e0ff86a1 Remove unused machine detection callback 2026-09-15 00:26:50 +09:00
Rui Ueyama 1e8dae17c2 Remove redundant file priority registry and DSO keep mask 2026-09-15 00:26:50 +09:00
Rui Ueyama aa4a728710 Remove unused target metadata registry 2026-09-15 00:26:12 +09:00
Rui Ueyama 47a35e8b38 Consolidate shared Cargo workspace settings 2026-09-14 23:21:26 +09:00
Rui Ueyama e2e85b3fc7 Document why architecture targets use separate crates 2026-09-14 23:01:08 +09:00
Rui Ueyama 3d67399506 Flatten ELF source modules 2026-09-14 22:58:05 +09:00
Rui Ueyama 946c71146d Rename architecture crate directory to arch 2026-09-14 22:54:34 +09:00
Rui Ueyama 972347aae9 Flatten the test directory and run scripts directly
Move shell cases and the runner directly under tests/, and remove the
standalone symbol output chunk test files and their references.

Make shell scripts executable and invoke them directly, honoring their
shebangs and execute permissions.
2026-09-14 22:54:34 +09:00
Rui Ueyama da0de56368 Flatten CLI source and integration test directories 2026-09-14 22:54:34 +09:00
Rui Ueyama 5e5db87568 Flatten target crate source directories 2026-09-14 22:54:34 +09:00
Rui Ueyama 3be3c7faef Borrow the separate debug output path 2026-09-14 22:54:34 +09:00
Rui Ueyama ef0e3553b4 Keep Context formatting beside its type definition 2026-09-14 22:54:34 +09:00
Rui Ueyama f58bf44acf Remove the unused SDA base symbol handle 2026-09-14 22:54:34 +09:00
Rui Ueyama 846d42aa73 Keep compressed sections with their destination indices 2026-09-14 22:54:34 +09:00
Rui Ueyama 66e7950766 Build complete symbol table writing jobs 2026-09-14 22:54:34 +09:00
Rui Ueyama d3c8f63e19 Use fixed arrays for output task buffers 2026-09-14 22:54:34 +09:00
Rui Ueyama fbe442097a Accumulate SFrame totals while gathering live entries 2026-09-14 22:54:34 +09:00
Rui Ueyama 2569831fbf Preserve borrowed filenames in background merging 2026-09-14 22:54:34 +09:00
Rui Ueyama ae5095177b Claim relocatable undefined symbols without a candidate vector 2026-09-14 22:54:34 +09:00
Rui Ueyama 433eca009e Iterate files directly in COMDAT and symbol version passes 2026-09-14 22:54:34 +09:00
Rui Ueyama bcba36dc9a Buffer dependency reports and reuse their symbol set 2026-09-14 22:54:34 +09:00
Rui Ueyama 92e2afcc28 Stream GC and ICF reports through buffered output 2026-09-14 22:54:34 +09:00
Rui Ueyama c3cea186f9 Build ICF report groups only for folded sections 2026-09-14 22:54:34 +09:00
Rui Ueyama 89596a87d6 Reuse ICF edge counts for prefix offsets 2026-09-14 22:54:34 +09:00
Rui Ueyama 6131f9b77b Collect only sections with unsorted relocations 2026-09-14 22:54:34 +09:00
Rui Ueyama c3cbd297fc Share thin archive member construction 2026-09-14 22:54:34 +09:00
Rui Ueyama 0380abbb7b Enumerate archive members lazily during target detection 2026-09-14 22:54:34 +09:00
Rui Ueyama b5aee2485e Find FDE parent CIEs by binary search 2026-09-14 22:54:34 +09:00
Rui Ueyama 6c504e434d Sort FDE records directly with stable cached keys 2026-09-14 22:54:34 +09:00
Rui Ueyama 87badfdb83 Construct relocation section names from bytes 2026-09-14 22:54:34 +09:00
Rui Ueyama 8fe5b00ea3 Infer timer nesting from completed intervals 2026-09-14 22:54:34 +09:00
Rui Ueyama b9d8f49037 Match the Windows LTO stub to the shared reader interface 2026-09-14 22:54:34 +09:00
Rui Ueyama db51683955 wip 2026-09-14 22:54:34 +09:00
Rui Ueyama fbb314400c Resolve remaining Clippy warnings without changing lock semantics 2026-09-14 22:54:34 +09:00
Rui Ueyama c0ef81a144 Refresh duplicated and outdated module documentation 2026-09-14 22:54:34 +09:00
Rui Ueyama 8c29736bd8 Keep Compressor formatting beside its type definition 2026-09-14 22:54:34 +09:00
Rui Ueyama 73c5095f1e Remove the unused path_dirname helper 2026-09-14 22:54:34 +09:00
Rui Ueyama 900d689cb3 Remove unused output section bookkeeping fields 2026-09-14 22:54:34 +09:00
Rui Ueyama fa71e1b222 Print timer children in their recorded order 2026-09-14 22:54:34 +09:00
Rui Ueyama ed2801b547 Format GDB input filenames only for diagnostics 2026-09-14 22:54:34 +09:00
Rui Ueyama 747a7508ca Skip empty inputs when scheduling GDB index work 2026-09-14 22:54:34 +09:00
Rui Ueyama 322056731f Reuse the ARM exception index buffer for final encoding 2026-09-14 22:54:34 +09:00
Rui Ueyama 48db48cda2 Format the thunk symbol suffix once per thunk 2026-09-14 22:54:34 +09:00
Rui Ueyama 385dadf43d Share target validation for object files and shared libraries 2026-09-14 22:54:34 +09:00
Rui Ueyama 2bfbce1933 Reuse chroot path handling for linker script sysroots 2026-09-14 22:54:34 +09:00
Rui Ueyama f08abb1b3c Build library candidate names once per search 2026-09-14 22:54:34 +09:00
Rui Ueyama f7edd50ff1 Borrow original arguments when restarting for LTO 2026-09-14 22:54:34 +09:00
Rui Ueyama ec231699c7 Move entry and initializer names into the symbol table 2026-09-14 22:54:34 +09:00
Rui Ueyama a50a793cda Narrow dynamic symbol insertion to its required tables 2026-09-14 22:54:34 +09:00
Rui Ueyama 557f3689c2 Borrow exported symbols while writing the GNU hash table 2026-09-14 22:54:34 +09:00
Rui Ueyama a78277b2f4 Reuse stable partitioning for DWARF section order 2026-09-14 22:54:34 +09:00
Rui Ueyama 5c5cba2179 Sort initializer sections with cached keys 2026-09-14 22:54:34 +09:00
Rui Ueyama 9f316eb875 Stream map reports through buffered output 2026-09-14 22:54:34 +09:00
Rui Ueyama 52b713b996 Retain generated merged section names only on insertion 2026-09-14 22:54:34 +09:00
Rui Ueyama 5053a1926e Retain only compressed section contents needed by GDB 2026-09-14 22:54:34 +09:00
Rui Ueyama ec685cb37a Consume output section builders after parallel collection 2026-09-14 22:54:34 +09:00
Rui Ueyama a026122121 Share file record layout guarantees across ELF traits 2026-09-14 22:54:34 +09:00
Rui Ueyama 1dede4350e Tie parallel output buffers to the lifetime of their storage 2026-09-14 22:54:34 +09:00
Rui Ueyama aed83a2109 Remove unused record parsing and misleading file helpers 2026-09-14 22:54:34 +09:00
Rui Ueyama d6bf14c5b3 Borrow version definition names from mapped linker scripts 2026-09-14 22:54:34 +09:00
Rui Ueyama 653406ff4d Assign final GDB name offsets in the existing layout pass 2026-09-14 22:54:34 +09:00
Rui Ueyama f50cfe847a Drop GDB input metadata after each file is parsed 2026-09-14 22:54:34 +09:00
Rui Ueyama 04f026cbcc Avoid copying stable chunk IDs, symbol IDs, and repro paths 2026-09-14 22:54:34 +09:00
Rui Ueyama da8e459060 Move reader state into archive results and deferred LTO jobs 2026-09-14 22:54:34 +09:00
Rui Ueyama abedadff59 Borrow unmodified response file tokens from mapped input 2026-09-14 22:54:34 +09:00
Rui Ueyama 96e6a38d08 Share expanded command line arguments through target dispatch 2026-09-14 22:54:34 +09:00
Rui Ueyama 40ad3e5700 Match C++ output file reuse detection with uname 2026-09-14 22:54:34 +09:00
Rui Ueyama 750523e694 Wrap CREL addend accumulation at signed integer boundaries 2026-09-14 22:54:34 +09:00
Rui Ueyama 86498df474 Assign task buffers by consuming their ordered iterator 2026-09-14 22:54:34 +09:00
Rui Ueyama 135b13f3cc Move claimed LTO COMDAT keys into their final storage 2026-09-14 22:54:34 +09:00
Rui Ueyama d52673605f Collect removed sections only when GC reporting is enabled 2026-09-14 22:54:34 +09:00
Rui Ueyama ffca665b28 Consume glob construction state into an immutable matcher 2026-09-14 22:54:34 +09:00
Rui Ueyama be74299a61 Borrow start and stop names that need no normalization 2026-09-14 22:54:34 +09:00
Rui Ueyama 74908a7035 Use memmem for literal byte substring searches 2026-09-14 22:54:34 +09:00
Rui Ueyama 7d1dee4a3a Format timer labels only when recording is enabled 2026-09-14 22:54:34 +09:00
Rui Ueyama b2775275f8 Format undefined reference messages without temporary strings 2026-09-14 22:54:34 +09:00
Rui Ueyama 4eb13f32c4 Borrow known ELF symbol and relocation type names 2026-09-14 22:54:34 +09:00
Rui Ueyama 96ea73e773 Borrow test jobs and the work counter in scoped threads 2026-09-14 22:54:34 +09:00
Rui Ueyama df5c9e146d Store the selected target triple in the test runner mode 2026-09-14 22:54:34 +09:00
Rui Ueyama 3d715f6571 Name the ARM instruction and data mapping kinds 2026-09-14 22:54:34 +09:00
Rui Ueyama a21598f0ef Remove unused bookkeeping fields 2026-09-14 22:54:34 +09:00
Rui Ueyama 4f04b640a8 Copy dynamic relocation offsets only when adjusting for RELR 2026-09-14 22:54:34 +09:00
Rui Ueyama c9cf514677 Count and write dynamic entries with a shared visitor 2026-09-14 22:54:34 +09:00
Rui Ueyama 2f7fd93117 Write GOT entries directly through a shared visitor 2026-09-14 22:54:34 +09:00
Rui Ueyama 0052475b7d Iterate over compression input chunks directly 2026-09-14 22:54:34 +09:00
Rui Ueyama a58c342b76 Flatten parallel results without per-group staging vectors 2026-09-14 22:54:34 +09:00
Rui Ueyama 77bce45d83 Borrow RISC-V extension names and serialize them into one string 2026-09-14 22:54:34 +09:00
Rui Ueyama 2cc4d6a81c Derive the debuglink filename from its option 2026-09-14 22:54:34 +09:00
Rui Ueyama 54f549c31e Read section and version indices directly from mapped bytes 2026-09-14 22:54:34 +09:00
Rui Ueyama 643bb2aefb Borrow shared object sonames from mapped storage 2026-09-14 22:54:33 +09:00
Rui Ueyama 3a62c12861 Iterate over dynamic entries without collecting intermediate vectors 2026-09-14 22:54:33 +09:00
Rui Ueyama c8e2be01ca Borrow archive names from their mapped files 2026-09-14 22:54:33 +09:00
Rui Ueyama a1d0c8a6a3 Borrow display filenames when the mapped path permits it 2026-09-14 22:54:33 +09:00
Rui Ueyama e21540ed26 Copy only unresolved state for background section merging 2026-09-14 22:54:33 +09:00
Rui Ueyama 0d3bc19530 Borrow DWARF section IDs and format diagnostic names lazily 2026-09-14 22:54:33 +09:00
Rui Ueyama 0faea2291f Rewrite and sort relocation tables in place 2026-09-14 22:54:33 +09:00
Rui Ueyama 9226b64d6d Borrow read-only section member and symbol lists 2026-09-14 22:54:33 +09:00
Rui Ueyama 0f9aea233b Walk unchanged chunk lists without cloning their IDs 2026-09-14 22:54:33 +09:00
Rui Ueyama 5430746f22 Borrow synthetic symbol option metadata 2026-09-14 22:54:33 +09:00
Rui Ueyama 055332c946 Keep internal ELF symbols in their owning object 2026-09-14 22:54:33 +09:00
Rui Ueyama 7340b1c35e Borrow forced symbol names during GC and LTO 2026-09-14 22:54:33 +09:00
Rui Ueyama a029e10c62 Consume version script and dynamic list option queues 2026-09-14 22:54:33 +09:00
Rui Ueyama c7632feaa4 Borrow retained symbol names from the mapped file 2026-09-14 22:54:33 +09:00
Rui Ueyama 22b16f16cd Move response token buffers into command-line arguments 2026-09-14 22:54:33 +09:00
Rui Ueyama b4867dd3ff Borrow command-line parser scratch arguments 2026-09-14 22:54:33 +09:00
Rui Ueyama 86ff43bf2d Reject -run on macOS as in C++ 2026-09-14 22:54:33 +09:00
Rui Ueyama 56b8bb797e Watch existing Git version inputs without watching the entire repository 2026-09-14 22:54:33 +09:00
Rui Ueyama c068d03df4 Use cc for Cargo-aware C compiler and archiver configuration 2026-09-14 22:54:33 +09:00
Rui Ueyama b8ae5a8c7d Locate wrapper output under custom Cargo profiles 2026-09-14 22:54:33 +09:00
Rui Ueyama d43a8b286a Match C++ detection of existing nondirectories for -R 2026-09-14 22:54:33 +09:00
Rui Ueyama 941a8d8d7c Initialize output buffers with the requested filler byte 2026-09-14 22:54:33 +09:00
Rui Ueyama 0395c30370 Match C integer syntax for numeric options 2026-09-13 21:47:20 +09:00
Rui Ueyama 8bbf7cf05c Apply chroot to thin archive members in every reader 2026-09-13 21:42:32 +09:00
Rui Ueyama da5c77278e Apply chroot when reading retained symbol names 2026-09-13 21:41:51 +09:00
Rui Ueyama b2ae03fdb8 Apply chroot to absolute output paths 2026-09-13 21:40:42 +09:00
Rui Ueyama b19ea1a396 Share immutable command-line arguments with background merging 2026-09-13 20:32:04 +09:00
Rui Ueyama d0f01784ff Borrow section names in cached sort keys 2026-09-13 20:30:50 +09:00
Rui Ueyama a1fedf5821 Apply dynamic-list matches directly to symbols in parallel 2026-09-13 20:30:09 +09:00
Rui Ueyama ace37f38f7 Borrow version and dynamic pattern metadata 2026-09-13 20:28:12 +09:00
Rui Ueyama 7fecd46e67 Keep parser tokens local and compare labels without allocating 2026-09-13 20:26:38 +09:00
Rui Ueyama c73f59645d Represent command-line target families with the existing enum 2026-09-13 20:25:21 +09:00
Rui Ueyama 9b051c1d43 Borrow canonical target names from static storage 2026-09-13 20:24:17 +09:00
Rui Ueyama dc127e5280 Accept vertical tabs as linker script whitespace 2026-09-13 20:22:41 +09:00
Rui Ueyama c9041967d1 Restore the default transparent huge page policy at startup 2026-09-13 20:21:19 +09:00
Rui Ueyama 99f51f03eb Bound legacy Rust demangler length arithmetic 2026-09-13 20:20:43 +09:00
Rui Ueyama c5a8e52925 Report output mapping failures instead of buffering zeroed data 2026-09-13 20:20:43 +09:00
Rui Ueyama df5a97ede2 Start with the first enabled linker target 2026-09-13 20:15:17 +09:00
Rui Ueyama dcf75d7d8b Represent object origins with a single enum 2026-09-13 20:15:17 +09:00
Rui Ueyama 6d2d640802 Cast LTO callbacks directly to C pointers 2026-09-13 20:15:17 +09:00
Rui Ueyama 6b502f1581 Format report lines directly into their output strings 2026-09-13 20:15:17 +09:00
Rui Ueyama a0fe942d2d Borrow option and dynamic strings directly from the context 2026-09-13 20:15:17 +09:00
Rui Ueyama a58e6f14ff Read environment flags without requiring UTF-8 2026-09-13 20:15:17 +09:00
Rui Ueyama 5e936482cb Preserve argument bytes in the embedded command line 2026-09-13 20:15:17 +09:00
Rui Ueyama 7ec6910413 Preserve bytes when forwarding LTO plugin options 2026-09-13 20:15:17 +09:00
Rui Ueyama 3cc86fe2c5 Keep archive paths and exclusion names in their original encoding 2026-09-13 20:15:17 +09:00
Rui Ueyama d74d0d0ef4 Preserve bytes in dynamic library names 2026-09-13 19:36:44 +09:00
Rui Ueyama 86dd63ead2 Distribute GDB index work through disjoint mutable chunks 2026-09-13 17:55:39 +09:00
Rui Ueyama 48ab9a44f3 Use zlib to calculate and combine Adler-32 checksums 2026-09-13 17:54:10 +09:00
Rui Ueyama 4fa0487aa3 Use memmem for glob literal substring searches 2026-09-13 17:53:17 +09:00
Rui Ueyama e09ce32f73 Use standard owners for the job lock and notification pipe 2026-09-13 17:52:55 +09:00
Rui Ueyama 2294f1d7f7 Represent report destinations with an enum 2026-09-13 17:51:10 +09:00
Rui Ueyama 160c059352 Store debug compression levels in their algorithm variants 2026-09-13 17:48:32 +09:00
Rui Ueyama 93d79e105f Borrow SONAMEs when sorting version requirements 2026-09-13 17:48:28 +09:00
Rui Ueyama d1e99e7a86 Intern argument symbols without cloning their option lists 2026-09-13 17:47:12 +09:00
Rui Ueyama 3d06f6484e Consume mutable slice prefixes with split_off_mut 2026-09-13 17:47:11 +09:00
Rui Ueyama 6f8aa2f99d Derive Clone for glob matcher components 2026-09-13 17:46:08 +09:00
Rui Ueyama b78feeaf7f Use a boxed slice for the serialized GDB index tables 2026-09-13 17:45:31 +09:00
Rui Ueyama cc01683a5c Keep output cleanup safe to call from signal handlers 2026-09-13 17:45:03 +09:00
Rui Ueyama 1a39c9318d Give Glob a default that matches an empty pattern set 2026-09-13 17:44:00 +09:00
Rui Ueyama 42d21ec936 Preserve bytes in symbol and section name options 2026-09-13 17:43:14 +09:00
Rui Ueyama 7449dc3873 Remove the redundant Context target marker
Context already uses E through its input files and output sections, so it
does not need a PhantomData field.

Validation: workspace check across all targets.
2026-09-13 15:41:51 +09:00
Rui Ueyama 7d6e9e8a26 Keep deferred files out of ReaderJob
Reserve ReaderJob for command-line inputs. Store deferred scripts and LTO
files as tuples with required mapped-file references, and remove unused
archive metadata from the command-line job. Move reader contexts into the
script queue while preserving command-line sorting.

Validation: workspace check; 28 script, archive and LTO tests passed
(two 32-bit prerequisite skips); clang, Chrome and TensorFlow debug
outputs match the baseline.
2026-09-13 15:41:51 +09:00
Rui Ueyama e82b5d9419 Use Once to register statistics counters
Replace the manual registration flag with std::sync::Once. Keep counter
updates atomic and skip registration when statistics are disabled.

Validation: workspace check; clang, Chrome and TensorFlow debug links
with matching counter values under --stats.
2026-09-13 15:41:51 +09:00
Rui Ueyama 283712d80d Make the LoongArch target marker a unit struct
Remove PhantomData<()> from LoongArchTarget. Its const parameter needs no
marker field, and the target remains a zero-sized type.

Validation: workspace check, including both LoongArch targets.
2026-09-13 15:24:28 +09:00
Rui Ueyama 81b0eb991d Store the shuffle seed in its enum variant
Replace the separate shuffle mode and seed fields with ShuffleSections.
Keep the last explicit seed while parsing, so a later bare shuffle option
reuses it even after --reverse-sections. Extend the existing seed test to
cover that option sequence.

Validation: workspace check; shuffle and reverse integration tests;
the new option sequence also passed before the implementation change.
2026-09-13 15:24:28 +09:00
Rui Ueyama a6a78bce25 Store section order data in enum variants
Replace SectionOrderKind and its separate fields with a SectionOrder
enum. Keep names or numbers in their variants and retain the original
token only for address diagnostics.

Validation: workspace check and existing section-order integration tests.
2026-09-13 15:10:15 +09:00
Rui Ueyama a221b33e4c Store glob token data in enum variants
Allocate character tables only for bracket expressions. Literal tokens
carry their bytes, and star and question tokens carry no data. Match the
variants directly when parsing patterns and building the NFA.

Validation: workspace check; four glob unit tests; 34 glob and version
script integration tests; clang, Chrome and TensorFlow debug benchmarks.
2026-09-13 15:08:16 +09:00
Rui Ueyama 03a77ed441 Store build ID data in enum variants
Replace BuildIdKind and the separate value/hash_size fields with one
BuildId enum. Each parsed option sets a complete variant, and note
emission reads its payload.

Validation: workspace check; existing build-id test; 10 checks covering
absent IDs, fixed byte values and option overrides.
2026-09-13 14:47:02 +09:00
Rui Ueyama be273267d6 Use one enum for relocation iteration
Remove the redundant struct and constructors. Keep the iterator opaque
and retain always-inline dispatch for ordinary and deferred CREL records.

Validation: workspace check; 15 relocation and GDB-index tests; three
large debug links with byte-for-byte identical output.
2026-09-13 14:45:00 +09:00
Rui Ueyama 7ffd95453f Use a u32 sentinel for ARM32 exception-index metadata
Replace Arm32InputSectionExtra with u32, using u32::MAX for an absent
.ARM.exidx section. Add InputSectionExtra::empty so ARM32 starts at the
sentinel instead of u32's default zero.
2026-09-13 14:17:24 +09:00
Rui Ueyama 714f673cbf Use a boxed slice for relaxation metadata
Implement InputSectionExtra directly for Box<[RelocDelta]> and use it on
RISC-V and LoongArch. Remove the wrapper that only forwarded access to
its boxed slice.
2026-09-13 14:15:32 +09:00
Rui Ueyama ba7760e427 Use the unit type for empty input-section metadata
Implement InputSectionExtra for () and use it on targets without extra
section state. Remove NoInputSectionExtra and its redundant size check.
2026-09-13 14:13:11 +09:00
Rui Ueyama c718a0def7 Use fmt::from_fn for lazy diagnostic formatting
Replace the section display wrapper and both file-name display wrappers
with formatting closures. Share the file-name formatter between input
parsing and merged-section diagnostics.
2026-09-13 14:11:38 +09:00
Rui Ueyama 2cb26dab38 Fill ICF edges through disjoint mutable slices
Use Rayon's recursive splitter and split_at_mut to give each task a
separate slice of the preallocated edge array. Preserve edge order and
the per-vertex count checks while removing EdgeBuffer and its unsafe
Sync implementation.
2026-09-13 13:59:01 +09:00
Rui Ueyama 5a87ab3a50 Collect thunk symbols with Rayon fold and reduce
Use owned vectors for parallel thunk symbol collection, removing
ThunkSymbolBins and its unsafe Sync implementation. Atomic symbol marking
still deduplicates entries, and the final sort preserves output order.
2026-09-13 13:39:08 +09:00
Rui Ueyama 5ec85a91bf Use standard mutexes for reader result buffers
Collect files and scripts in per-worker vectors, locking only to append
completed results. Consume the bins after the scoped jobs join and restore
command line order as before.
2026-09-13 13:24:53 +09:00
Rui Ueyama 1c097506eb Use standard mutexes for COMDAT work buffers
Replace ComdatWorkBins with per-worker mutexes, locking once per input file.
Remove the custom UnsafeCell wrapper while preserving buffer reuse.
2026-09-13 13:24:53 +09:00
Rui Ueyama b057cc82be Use standard mutexes for output section caches
Keep one cache per Rayon worker and lock it once per input file. Remove the
custom UnsafeCell wrapper and its unsafe Sync implementation while
preserving cache reuse across jobs.
2026-09-13 13:08:41 +09:00
Rui Ueyama 67f9388285 Use u32 sentinels for optional symbol indices
Use u32::MAX for absent entries and store zero-based indices directly.
Initialize all optional slots explicitly and keep SymbolAux at 64 bytes.
2026-09-13 13:08:41 +09:00
Rui Ueyama 7f640167a2 Use a raw slice pointer for mapped file bytes 2026-09-13 12:51:54 +09:00
Rui Ueyama b7e53ea5e6 Use standard ranges for output file slices 2026-09-13 12:49:58 +09:00
Rui Ueyama e3a89f222d Reject overflowing decimal defsym values
Report a parse error instead of silently defining address zero when a
decimal --defsym value exceeds u64::MAX, as for hexadecimal values.

Test the largest valid decimal and hexadecimal values, zero, and values
beyond the limit. The new regression fails with the original Rust linker;
all seven defsym integration tests pass with this fix.
2026-09-13 12:43:30 +09:00
Rui Ueyama 60ba943a6f Sanitize start and stop symbol names byte by byte
Keep generated names as bytes and replace each non-ASCII-alphanumeric byte
with an underscore, matching C++ mold for valid and invalid UTF-8 input.

Add a regression that links references to ordinary start/stop symbols.
It passes on all 14 available target configurations and with C++ mold,
and fails with the original Rust linker.
2026-09-13 12:43:30 +09:00
Rui Ueyama 833be2a105 Preserve bytes in aliases and retained symbol names
Keep linker-script assignments, --defsym definitions and targets, retained
names, and forced undefined symbols as byte vectors through lookup and GC.
Accept raw command-line symbol names and trim retain-file entries like C++ mold.

Add a regression with distinct invalid UTF-8 and replacement-character names,
raw alias names, archive extraction, and GC roots. It passes with the Rust and
C++ linkers and fails with the original Rust linker. Existing defsym and
retain-symbols tests also pass.
2026-09-13 12:43:30 +09:00
Rui Ueyama 85a3c6f135 Use a portable operating system random source
Replace the unconditional /dev/urandom read with getrandom so ICF, UUID build
IDs, and unseeded section shuffling also work on Windows.
2026-09-13 12:43:26 +09:00
Rui Ueyama 3dfe91ce8b Document the FrozenMap entry ID invariant
Explain that linker lookups use published entry IDs from the owning map and
never probe nonexistent entries.
2026-09-13 11:00:35 +09:00
Rui Ueyama d756118ab3 Use atomic storage for COMDAT ownership
Store the temporary winning file priority in an actual AtomicU32 instead of
casting an immutable u32 field to an atomic reference. Keep ordinary
symbol-index access behind getters and an exclusive setter.

Add a concurrent ownership regression verified under Miri with both Stacked
Borrows and Tree Borrows.
2026-09-13 11:00:35 +09:00
Rui Ueyama a6d4f75da8 Use boxed ownership and live indices in FileList
Store files in Vec<Box<T>> and live entries as u32 pool indices. Resolve
access through the pool so indexed access and live iteration do not rely
on cached pointers across Box borrows.

Use disjoint pool slices for mutable and parallel iteration, preserving
stable file indices while letting Box and Vec manage ownership.
2026-09-13 11:00:04 +09:00
Rui Ueyama 301382e5a2 Merge pull request #1653 from gruenich/feature/update-github-actions 2026-09-12 16:37:00 +09:00
Christoph Grüninger 4e56b6d371 [ci] Update GitHub Actions podman-login to v2
Fix deprecation warning for Node.js 20.
Further, update actions/checkout to v7.
2026-09-12 08:32:23 +02:00
Rui Ueyama 6968a402a1 Abort on panic in release builds
Use the abort panic strategy for release builds to reduce generated
code. A panic now terminates the process without unwinding the stack.

An isolated five-run comparison across three workloads reduced .text
size by 12.7% on the local x86-64 machine and 13.6% on Fedora AArch64.
The measured link-time differences were small relative to run-to-run
variation.

Validated release builds for all 20 targets and successful links for
all three workloads on both machines.
2026-09-11 20:10:53 +09:00
Rui Ueyama af8c689722 Bump mold version to 2.42.1
Update all workspace package versions and the lockfile together.

Upstream-Commit: 9b376bc6a9
2026-09-11 17:18:54 +09:00
Rui Ueyama 9b376bc6a9 Bump mold version to 2.42.1 2026-09-11 15:56:48 +09:00
Rui Ueyama 7a7d03d186 Skip timer recording when profiling is disabled
Avoid clock reads, getrusage calls and shared record locks during ordinary
links. Explicit --perf keeps the existing timing output. Cover nested timers,
background handles and repeated stops in a unit test.
2026-09-11 14:08:17 +09:00
Rui Ueyama f359d51458 Preallocate only the appended output range
Match C++ output growth by passing the old length and appended size to
fallocate. Revisiting the already-written prefix flushes dirty extents on btrfs
and adds about a second to Chromium debug links on Fedora. The GDB-index suite
passes across available targets, and the change is neutral on ext4.
2026-09-11 14:08:17 +09:00
Rui Ueyama a25435ec43 Accept PPC64 function descriptors in path regression tests
Inspect ELF symbol type and definition with readelf instead of requiring nm to
label foo as text. PPC64 ELFv1 function symbols name descriptors in .opd and
correctly appear as data to nm. This lets the existing path and reproduction
tests run across the full target suite.
2026-09-11 14:08:17 +09:00
Rui Ueyama f6265afa79 Don't let an archive member shadow a later object file's definition
Initialize reachability when creating object, LTO, and shared inputs so
mandatory objects outrank unextracted archive members during initial resolution.
Process every --undefined-glob match, including multiple matches in reachable
files, so GC retains each matching definition. Port both upstream regressions
verbatim.

Upstream-Commit: af7d056853
2026-09-11 14:08:17 +09:00
Rui Ueyama 3fd572410d Hint uncommon symbol address cases
Mark dead fragments, copy relocations, and dead input sections as cold paths,
matching the upstream branch hints while retaining ICF leader resolution and
discarded-section diagnostics.

Upstream-Commit: 5bdd5e6284
2026-09-11 14:08:17 +09:00
Rui Ueyama c39ecd6e07 Compute imported-symbol weakness while marking imports
Collect strong undefined references during the existing import/export traversal
and apply binding changes together with the import flags. Remove the later full
object-symbol scan, preserving weak binding when every undefined reference is
weak.

Upstream-Commit: a4bcbb45ae
2026-09-11 14:08:17 +09:00
Rui Ueyama db7753b997 Cache merged output sections per worker
Remember up to 32 merged-section keys and IDs in each conversion worker,
avoiding the shared lock on cache hits. Copy immutable keys rather than
retaining pointers into the growable section vector, and discard the cache when
conversion finishes.

Upstream-Commit: 4a83b5d856
2026-09-11 14:08:17 +09:00
Rui Ueyama 6c27ac899c Gather shared-library symbol names in parallel
Record shared-library names and default-version aliases directly into worker
bins during parsing. Reserve both symbol vectors first so their recorded
slots stay stable until the existing parallel gather fills them. FileList
retains the backing files through SONAME deduplication. This removes the
temporary name arrays and the later registration pass.

Upstream-Commit: 20eb664a7e
2026-09-11 14:08:17 +09:00
Rui Ueyama 725db8a81a Avoid redundant atomic OR operations
Load flag bytes before setting bits, skipping the read-modify-write when all
requested bits are already present. Apply the shared helper to symbol and
input-section setters; keep fetch_or for operations that need the previous
value.

Upstream-Commit: c72d19dcca
2026-09-11 13:14:25 +09:00
Rui Ueyama af7d056853 Don't let an archive member shadow a later object file's definition
Initialize reachability when creating object, LTO, and shared inputs so
mandatory objects outrank unextracted archive members during the first
symbol resolution pass.

Process all --undefined-glob matches regardless of file reachability so
--gc-sections retains every matching definition. Cover archive ordering,
directly linked objects, and multiple matches in one object.
2026-09-10 14:45:33 +09:00
Rui Ueyama 5bdd5e6284 Hint uncommon symbol address cases 2026-09-10 14:45:31 +09:00
Rui Ueyama a4bcbb45ae Compute imported-symbol weakness while marking imports
The removed pass took a few milliseconds; the end-to-end change is
within noise.
2026-09-10 14:45:24 +09:00
Rui Ueyama 4a83b5d856 Cache merged output sections per worker
MergedSection::get_instance took a shared lock for every mergeable
input section, though nearly all of them resolve to the same few
output sections. Each worker now remembers up to 32 sections it has
already resolved, keyed on the same name, flags, type and entry size,
and checks them before taking the lock. The cache lives only for the
conversion pass, during which those fields are immutable.

Geometric-mean link time over the 21-program suite fell 0.87% at 32
threads on a Threadripper 7980X and 3.88% at 20 threads on an M1
Ultra, where create_merged_sections on clang-release went from 24 ms
to 10 ms.
2026-09-10 14:45:03 +09:00
Rui Ueyama 20eb664a7e Gather shared-library symbol names in parallel
Shared libraries inserted their symbol names into the symbol map one
at a time on the parsing thread, leaving a serial tail behind large
DSOs. Their unversioned names now go through the same per-thread bins
as object-file symbols, so gather_symbols creates the canonical
Symbols in parallel across hash shards. The symbol vector is reserved
up front, so the recorded slots stay valid until the gather fills them.

Geometric-mean link time over the 21-program suite fell 1.41% at 32
threads on a Threadripper 7980X, almost all of it from blender-release
(0.177 s to 0.142 s), which links against many shared libraries.
2026-09-10 14:44:31 +09:00
Rui Ueyama c72d19dcca Avoid redundant atomic OR operations
Relocation scanning requests GOT, PLT and TLS entries for the same
symbols repeatedly, and each request took exclusive ownership of the
symbol's cache line even when the bits were already set. Atomic's
operator|= now loads first and skips the read-modify-write when no
bit would change. fetch_or stays unconditional for callers that need
it.

Measured before the check moved from a symbol-specific helper into
the wrapper: geometric-mean link time over the 21-program suite fell
1.95% at 32 threads on a Threadripper 7980X and 2.71% on an M1 Ultra.
2026-09-10 14:44:16 +09:00
Rui Ueyama 1967c81832 Preserve Unix path bytes through argument and file handling
Use OsString for arguments and PathBuf for filesystem names so valid Unix paths
survive parsing and reach the filesystem unchanged. Preserve their bytes in
loader metadata, LTO callbacks, reproduction archives and dependency rules as
well.

Add a regression using compiler-generated objects with non-UTF-8 bytes and
spaces in their names. Cover direct arguments, nested response files, library
search, linker scripts, thin archives and reproduction replay.
2026-09-09 14:16:13 +09:00
Rui Ueyama 05ee232da0 Resolve symbol output chunks through the owning context
Store compact chunk IDs in symbol origins instead of pointers with erased
lifetimes and target types. Resolve each ID through an append-only context
registry, preserving the 48-byte symbol layout and removing the raw pointer
dereference.

Add runtime tests for context moves, chunk reordering and 32-bit layouts, plus
compile-fail tests for dangling and incorrectly typed references. The unit
tests, API tests, doctests and 15 related native linker tests pass.
2026-09-09 13:48:39 +09:00
Rui Ueyama 71401cb632 Preserve shared library SONAMEs as byte strings
Keep SONAME bytes intact during library deduplication, dependency checks and
dynamic string and version-table output. Distinct non-UTF-8 names must retain
distinct library identities.

Add two versioned libraries whose names previously collapsed under lossy UTF-8
decoding. Verify both names in .dynstr and run the linked program. All 43 native
SONAME, dependency and version-script tests pass.
2026-09-09 13:43:05 +09:00
Rui Ueyama 6f151f5889 Preserve symlink input names in reproduction archives
Make archive member paths absolute without resolving symlinks, matching the
names retained in response.txt. Reopen each input to copy its original contents
as before.

Add a regression that extracts and replays archives for a relative symlink and
an absolute path through a symlinked directory. Both native reproduction tests
pass.
2026-09-09 13:36:21 +09:00
Rui Ueyama cf3ad924aa Check nested linker script targets during library search
Use the reader context to determine the target of a nested script before
accepting it. This lets an incompatible script beside its parent be skipped in
favor of a compatible script in a later library directory.

Add an x86-64 regression with competing i386 and x86-64 nested scripts. It fails
before the fix and passes in C++ mold; all 15 native linker-script tests pass.
2026-09-09 13:34:20 +09:00
Rui Ueyama 434a0d7499 Read Zstd-compressed DWARF headers with streaming decompression
Stop decompression when the requested output buffer is full, allowing the
twelve-byte DWARF classification probe to read a prefix of a larger frame.
Preserve the premature-end diagnostic for short output.

Add a test with verified Zstd-compressed DWARF32 and DWARF64 objects and check
their output order. The new test fails before the fix and passes in C++ mold.
All seven native Zstd and debug-sorting tests pass.
2026-09-09 13:33:27 +09:00
Rui Ueyama 0380bea1dc Parse attached visibility labels in embedded version scripts
Recognize global:foo and local:* in the shared version-command parser so VERSION
blocks behave like standalone version scripts. Keep colons intact in input
filenames.

Add a regression covering local and global visibility and colon-containing input
names. Discover the new test/ directory alongside the existing shell suite. All
32 native version-script tests pass.
2026-09-09 13:31:47 +09:00
Rui Ueyama 9c7f14afdf Document upstream synchronization and release performance
Record how all 27 upstream commits are represented in Rust, including
existing equivalents and C++-only changes. Describe the Rust-specific
optimizations and the completed build, integration, unit-test and static
checks.

Document the 21-workload release benchmark, build settings and noise
rejection criteria. Include raw samples supporting the measured 6.1%
improvement over the original Rust build and the remaining 2.9%
geometric-mean gap to C++.
2026-09-09 10:38:33 +09:00
Rui Ueyama 3898598581 Inline common symbol address calculation
Inline addr_with and the small auxiliary-index accessors used during
relocation and symbol-table writes. Move rare discarded-section and
.eh_frame diagnostics into a cold, non-inlined helper while retaining
ICF leader handling in the common address path.

Read the auxiliary record once when checking for PLT entries. These
changes reduce call and lookup overhead without changing computed
addresses or diagnostic behavior.
2026-09-09 10:38:29 +09:00
Rui Ueyama 9196e3821d Inline literal-pattern automaton transitions
Force AhoCorasick::walk into its caller because it runs once per input
byte while matching symbol names. Keeping the state index and current
priority in the caller's registers removes per-byte call overhead. The
transition logic and highest-priority match rules stay unchanged.
2026-09-09 10:38:23 +09:00
Rui Ueyama 9494e857ac Avoid redundant reachability writes and empty GC allocations
Use mark_reachable for shared-library edges so repeatedly visiting an
already reachable DSO does not keep writing its reachability flag. This
avoids unnecessary cache-line traffic during parallel traversal.

Allocate child-work buffers only when a batch discovers new sections,
and transfer full buffers with mem::take. Exported symbols can already
make most sections roots, leaving many batches with no children; those
batches no longer need a heap allocation.
2026-09-09 10:38:18 +09:00
Rui Ueyama 9ae2154d65 Hash complete ICF digest words directly
Feed ICF digest words directly to SipHash when its byte buffer is empty,
avoiding temporary byte arrays and buffering work during propagation.
Keep the byte-update fallback for partial buffers, preserve the native
digest byte order and wrapping length counter, and inline the hot update
and finalization paths.

Compare word updates with byte updates across partial prefixes, scalar
tails and length wraparound to verify that the digest is unchanged.
2026-09-09 10:38:14 +09:00
Rui Ueyama 5ce808338e Pack optional symbol auxiliary indices into four bytes
Replace the eight Option<u32> fields in SymbolAux with four-byte indices
using u32::MAX for absence. This removes separate discriminants and
reduces each auxiliary record from 96 to 64 bytes. Use u32::MAX - 1 for
the provisional dynamic-symbol index so it remains distinct from the
missing-entry sentinel.

Update table insertion and lookup helpers, assert the record size, and
test absent indices, index zero, the provisional value and reuse.
2026-09-09 10:38:08 +09:00
Rui Ueyama e36bf62474 Fix -Wrestrict warning
Upstream changes temporary-output-name construction to avoid a GCC
-Wrestrict warning from C++ string concatenation. Rust already builds
these names through formatting and has no matching expression to fix.
This empty commit records that no Rust change is needed.

Upstream-Commit: dad2695f3e
2026-09-09 10:37:58 +09:00
Rui Ueyama b20d780768 Upgrade Debian 11 to Debian 12
Debian 11 LTS has ended, and its apt-get started failing on CI.

Upstream-Commit: fcd9dea78f
2026-09-09 10:37:54 +09:00
Rui Ueyama 47513055fb Size the CREL section table once
Allocate decoded_crel for all section indices on the first CREL section.
This avoids repeatedly growing and copying the table during section
parsing. Files without CREL allocate no table.

Upstream-Commit: ff0cb501b0
2026-09-09 10:37:51 +09:00
Rui Ueyama d4a2b4b63c Skip redundant COMDAT state updates for newly parsed files
Parsing already omits losing COMDAT members and marks retained sections
alive, so skip redundant state updates for newly parsed files. Keep the
member walk when reusing parsed files, when LTO can change ownership, or
with --gdb-index.

Upstream-Commit: 91252d8aa5
2026-09-09 10:37:47 +09:00
Rui Ueyama e8a68b1bbb Use larger thread-local arena blocks
Upstream increases per-thread arena blocks from 64 KiB to 1 MiB to
reduce global reservations, retaining the local-allocation cutoff at one
quarter of a block. Rust uses vectors and mimalloc rather than that
arena, so there is no corresponding block-size setting to change. Record
the C++ change with an empty commit.

Upstream-Commit: 4a11b7849d
2026-09-09 10:37:43 +09:00
Rui Ueyama 46efea10eb Scan global symbol names once during registration
Populate name-length caches during symbol registration. Scan global
names up to '@' or NUL, then scan the version suffix only when present,
retaining the raw length before version normalization or --wrap. This
removes a separate cache-population pass for both regular and LTO
objects.

Upstream-Commit: 53715c07ee
2026-09-09 10:37:39 +09:00
Rui Ueyama 5dfefa867a Inline small dynamic table insertion methods
Mark the .dynsym, .plt and .plt.got add_symbol functions inline so the
compiler can expand their calls in the serial table-allocation loop.
This exposes the small insertion helpers to optimization while
preserving table insertion order and index assignment.

Upstream-Commit: 41d2f376c3
2026-09-09 10:37:35 +09:00
Rui Ueyama 0800e0ed0d Reuse cached version-suffix checks for COMDAT signatures
Reuse the version-suffix flag set during global-symbol registration
instead of scanning COMDAT signature names for '@' again. Handle a bare
trailing '@' separately because it does not set the flag.

Upstream-Commit: 3a58944ff9
2026-09-09 10:37:32 +09:00
Rui Ueyama 28ddca3727 Check dynamic-symbol version conflicts in parallel
Check dynamic-symbol version conflicts in parallel, using existing
symbol-map locking and synchronized diagnostics. Keep the error
checkpoint after all checks finish.

Upstream-Commit: ec7c803207
2026-09-09 10:37:28 +09:00
Rui Ueyama 81054189b7 Allocate fragment symbols while reattaching sections
Create fragment records as section-symbol relocations are reattached,
collecting only the records each file needs in relocation order. Reserve
exactly their final slots in the central symbol vector, then construct
symbols and append their IDs to each file in parallel. This removes the
preliminary relocation-counting pass and unused dummy symbols. Retain
the cheap CREL-header capacity bound to avoid moving existing symbols.

Upstream-Commit: f9bdf4b4de
2026-09-09 10:37:24 +09:00
Rui Ueyama 7d98a49a2f Populate independent symbol-table ranges concurrently
Rust already runs the synthesized-symbol, object-file and shared-file
symbol-table writers concurrently. Their symbol, string and
extended-section-index ranges are disjoint, so independent writers need
no shared append point. This empty commit records that the upstream
parallelization is already present.

Upstream-Commit: e9de4def18
2026-09-09 10:37:18 +09:00
Rui Ueyama fc3271050f Reuse dynamic string metadata in both output writers
Keep each dynamic symbol's name and string offset after sizing .dynstr,
reusing cached GNU hash sort keys when available. Both output writers
can then use independent parallel loops without repeating prefix scans
or name lookups. The cache costs 24 bytes per dynamic symbol.

Upstream-Commit: 18d173697c
2026-09-09 10:37:14 +09:00
Rui Ueyama 5963506d8d Cache exact symbol name lengths without enlarging Symbol
Rust already caches exact u32 symbol name lengths while keeping Symbol
at 48 bytes. Its name accessors use that cache without rescanning long
names, so there is no corresponding layout or lookup change to apply.
This empty commit records the upstream equivalence.

Upstream-Commit: fa1e8edc39
2026-09-09 10:37:09 +09:00
Rui Ueyama 01b59b2155 Use a flat lookup table for symbol-map shards
Rust already uses flat hashbrown tables for symbol-map shards. Compare
cached hashes before strings and fold higher hash bits into bucket
placement so fixed shard-selection bits do not concentrate keys in the
same buckets. Preserve entropy in the high-bit lookup tags and keep
symbol storage stable while reducing lookup overhead.

Upstream-Commit: 841880212f
2026-09-09 10:37:05 +09:00
Rui Ueyama 2316c952ad Merge and lay out non-allocated strings in the background
Resolve non-allocated merged sections and assign their offsets in a
background Rayon task while symbol processing, GC and relocation
scanning continue. Give the task owned metadata and immutable input
bytes, create .comment before starting it, and publish the result before
section sorting and sizing. Keep allocated-fragment layout after GC, and
reassign offsets when debug sections are reordered.

Upstream-Commit: f585fee6ca
2026-09-09 10:36:56 +09:00
Rui Ueyama f42386cf28 Assign defined-symbol version indices in parallel
Fill .gnu.version slots in parallel after allocating the array. Each
dynamic symbol owns a distinct slot, so these writes are independent.

Upstream-Commit: 95137b235e
2026-09-09 10:36:50 +09:00
Rui Ueyama abc06691d2 Collect GC roots in per-thread buffers
Rust already collects newly marked GC roots in local Rayon worker
vectors and combines them before graph traversal. This avoids contention
on a shared concurrent vector, matching the upstream change. No source
change is needed; this empty commit records that equivalence.

Upstream-Commit: 95dbca6708
2026-09-09 10:36:47 +09:00
Rui Ueyama 6d3e8f3ff4 Stop version matching once the result is determined
Give consecutive version-script patterns with the same version index one
priority. Stop matching when no higher-priority result is possible, and
check simple substring patterns before glob patterns.

Upstream-Commit: f8cb3daf00
2026-09-09 10:36:43 +09:00
Rui Ueyama 875e3541c4 Hash mergeable fragments while splitting sections
Compute fragment hashes and cardinality estimates during the string or
record scan, avoiding a second traversal of the input contents and
fragment offsets.

Upstream-Commit: 0f582b0a23
2026-09-09 10:36:37 +09:00
Rui Ueyama ea8ee584b5 Speed up consecutive debug relocations
Debug relocations often visit consecutive merged strings. Cache the last
symbol and mergeable-section metadata in each relocation stream, so
input sections do not share mutable lookup state. Try the next fragment
before binary search and prefetch upcoming entries.

Upstream-Commit: 2c402f707f
2026-09-09 10:36:33 +09:00
Rui Ueyama e81d70eebd Hash GNU symbol names four bytes at a time
Process four unsigned bytes per iteration and factor the DJB recurrence
into pairs to shorten its dependency chain. Hash the remaining bytes
with the scalar loop.

Upstream-Commit: 046a644906
2026-09-09 10:36:25 +09:00
Rui Ueyama 4037f207fe Partition dynamic symbols in parallel
Add a stable partition helper that evaluates predicates and scatters
elements into disjoint output ranges in parallel. Use it for the
local/global and import/export partitions in sort_dynsyms, preserving
the relative order within both groups. Evaluate predicates sequentially
for small ranges.

Upstream-Commit: 8566358143
2026-09-09 10:36:20 +09:00
Rui Ueyama d09c44da9a Parallelize PLT symbol string sizing
Sum PLT symbol string sizes in parallel so large PLTs do not make
symbol-table sizing wait for a single thread.

Upstream-Commit: 477db5f372
2026-09-09 10:36:14 +09:00
Rui Ueyama f7a381610c Prefetch symbols during dynamic table allocation
Prefetch symbol records ahead of their auxiliary records to hide the two
dependent pointer loads in the serial table-allocation loop.

Upstream-Commit: 9887339f72
2026-09-09 10:36:10 +09:00
Rui Ueyama a3d83c5327 Allocate dynamic symbol auxiliary data in parallel
Keep dynamic symbols grouped by their owning input file so auxiliary
records can be initialized and indexed in parallel without globally
sorting symbol IDs. File ownership makes writes independent. Preserve
first-use order within each file, suppress repeated IDs, and retain
existing auxiliary data before the serial table-allocation loop.

Upstream-Commit: 0706198355
2026-09-09 10:36:06 +09:00
Rui Ueyama 65279c9624 Cache dynamic symbol sort keys in a contiguous array
Cache GNU hash bucket indices and name views before sorting dynamic
symbols. This avoids repeated pointer chasing, name-length lookups and
bucket calculations in the comparison function.

Upstream-Commit: cbcf79c0ef
2026-09-09 10:35:55 +09:00
Rui Ueyama 613422a00e Parallelize dynamic symbol and string table generation
Compute string offsets with parallel scans and write dynamic symbols and
strings in parallel. This removes serial work on large dynamic symbol
tables while preserving output order.

Upstream-Commit: 6060657622
2026-09-09 10:35:07 +09:00
Rui Ueyama dad2695f3e Fix -Wrestrict warning 2026-09-08 23:16:43 +09:00
Rui Ueyama fcd9dea78f Upgrade Debian 11 to Debian 12
Debian 11 LTS has ended, and its apt-get started failing on CI.
2026-09-08 22:52:43 +09:00
Rui Ueyama ff0cb501b0 Size the CREL section table once
Allocate decoded_crel for all section indices on the first CREL section.
This avoids repeatedly growing and copying the table during section
parsing. Files without CREL allocate no table.
2026-09-08 22:11:12 +09:00
Rui Ueyama 91252d8aa5 Skip redundant COMDAT state updates for newly parsed files
Parsing already omits losing COMDAT members and marks retained sections
alive, so skip redundant state updates for newly parsed files. Keep the
member walk when reusing parsed files, when LTO can change ownership, or
with --gdb-index.
2026-09-08 22:11:12 +09:00
Rui Ueyama 4a11b7849d Use larger thread-local arena blocks
Increase the per-thread arena block from 64 KiB to 1 MiB to reduce
global reservations. Keep the local-allocation cutoff at one quarter of
a block.
2026-09-08 22:11:12 +09:00
Rui Ueyama 53715c07ee Scan global symbol names once during registration
Populate name-length caches during symbol registration. Scan global
names up to '@' or NUL, then scan the version suffix only when present,
retaining the raw length before version normalization or --wrap. This
removes a separate cache-population pass for both regular and LTO
objects.
2026-09-08 22:11:12 +09:00
Rui Ueyama 41d2f376c3 Inline small dynamic table insertion methods
Move the .dynsym, .plt and .plt.got add_symbol definitions into their
classes so the compiler can inline calls in the serial table-allocation
loop.
2026-09-08 22:11:12 +09:00
Rui Ueyama 3a58944ff9 Reuse cached version-suffix checks for COMDAT signatures
Reuse the version-suffix flag set during global-symbol registration
instead of scanning COMDAT signature names for '@' again. Handle a bare
trailing '@' separately because it does not set the flag.
2026-09-08 22:11:12 +09:00
Rui Ueyama ec7c803207 Check dynamic-symbol version conflicts in parallel
Check dynamic-symbol version conflicts in parallel, using existing
symbol-map locking and synchronized diagnostics. Keep the error
checkpoint after all checks finish.
2026-09-08 22:11:12 +09:00
Rui Ueyama f9bdf4b4de Allocate fragment symbols while reattaching sections
Create fragment symbols in the arena as section-symbol relocations are
reattached. Collect pointers locally and reserve the file's symbol
vector once before appending them in relocation order, eliminating the
preliminary counting pass.
2026-09-08 22:11:12 +09:00
Rui Ueyama e9de4def18 Populate independent symbol-table ranges concurrently
Run synthesized-symbol, object-file and shared-file symbol-table writers
concurrently. They write disjoint symbol, string and
extended-section-index ranges; initialize section symbols before
starting these writers.
2026-09-08 22:11:12 +09:00
Rui Ueyama 18d173697c Reuse dynamic string metadata in both output writers
Keep each dynamic symbol's name and string offset after sizing .dynstr,
reusing cached GNU hash sort keys when available. Both output writers
can then use independent parallel loops without repeating prefix scans
or name lookups. The cache costs 24 bytes per dynamic symbol.
2026-09-08 22:11:12 +09:00
Rui Ueyama fa1e8edc39 Cache exact symbol name lengths without enlarging Symbol
Store a signed 32-bit name length in Symbol's existing padding to avoid
rescanning long names. Saturate at INT32_MAX and scan the suffix only
for saturated lengths.
2026-09-08 22:11:12 +09:00
Rui Ueyama 841880212f Use a flat lookup table for symbol-map shards
Replace per-key unordered_map nodes with linear-probing slots containing
cached hashes and pointers to arena entries. This reduces lookup
overhead while keeping symbol addresses stable.
2026-09-08 22:11:12 +09:00
Rui Ueyama f585fee6ca Merge and lay out non-allocated strings in the background
Resolve non-allocated merged sections and assign their offsets in a
low-priority TBB task while symbol processing, GC and relocation
scanning continue. Create .comment before starting the task and join
before section sorting and sizing consume the results. Keep
allocated-fragment layout after GC, and reassign offsets if debug
sections are reordered.
2026-09-08 22:11:12 +09:00
Rui Ueyama 95137b235e Assign defined-symbol version indices in parallel
Fill .gnu.version slots in parallel after allocating the array. Each
dynamic symbol owns a distinct slot, so these writes are independent.
2026-09-08 22:11:12 +09:00
Rui Ueyama 95dbca6708 Collect GC roots in per-thread buffers
Collect newly marked root sections in per-thread vectors, then flatten
them before graph traversal. This avoids contention on the shared
concurrent vector.
2026-09-08 22:11:12 +09:00
Rui Ueyama f8cb3daf00 Stop version matching once the result is determined
Give consecutive version-script patterns with the same version index one
priority. Stop matching when no higher-priority result is possible, and
check simple substring patterns before glob patterns.
2026-09-08 22:11:12 +09:00
Rui Ueyama 0f582b0a23 Hash mergeable fragments while splitting sections
Compute fragment hashes and cardinality estimates during the string or
record scan, avoiding a second traversal of the input contents and
fragment offsets.
2026-09-08 22:11:12 +09:00
Rui Ueyama 2c402f707f Speed up consecutive debug relocations
Debug relocations often visit consecutive merged strings. Cache the last
symbol and mergeable section per thread, keyed by input section and
symbol index. Try the next fragment before binary search and prefetch
upcoming entries.
2026-09-08 22:11:12 +09:00
Rui Ueyama 046a644906 Hash GNU symbol names four bytes at a time
Process four unsigned bytes per iteration and factor the DJB recurrence
into pairs to shorten its dependency chain. Hash the remaining bytes
with the scalar loop.
2026-09-08 22:11:12 +09:00
Rui Ueyama 8566358143 Partition dynamic symbols in parallel
Add a stable partition helper that evaluates predicates and scatters
elements in parallel. Use it for the local/global and import/export
partitions in sort_dynsyms, keeping the standard algorithm for small
ranges.
2026-09-08 22:11:12 +09:00
Rui Ueyama 477db5f372 Parallelize PLT symbol string sizing
Sum PLT symbol string sizes in parallel so large PLTs do not make
symbol-table sizing wait for a single thread.
2026-09-08 22:11:12 +09:00
Rui Ueyama 9887339f72 Prefetch symbols during dynamic table allocation
Prefetch symbol records ahead of their auxiliary records to hide the two
dependent pointer loads in the serial table-allocation loop.
2026-09-08 22:11:12 +09:00
Rui Ueyama 0706198355 Allocate dynamic symbol auxiliary data in parallel
Initialize SymbolAux in the per-file collection pass, where symbol
ownership makes the writes independent. This reduces work in the serial
allocation loop.
2026-09-08 22:11:12 +09:00
Rui Ueyama cbcf79c0ef Cache dynamic symbol sort keys in a contiguous array
Cache GNU hash bucket indices and name views before sorting dynamic
symbols. This avoids repeated pointer chasing, name-length lookups and
bucket calculations in the comparison function.
2026-09-07 19:02:32 +09:00
Rui Ueyama 6060657622 Parallelize dynamic symbol and string table generation
Compute string offsets with parallel scans and write dynamic symbols and
strings in parallel. This removes serial work on large dynamic symbol
tables while preserving output order.
2026-09-07 19:02:32 +09:00
Rui Ueyama b11bcd8e6d Move deferred LTO archive names into object files
Deferred LTO jobs already own archive names, but claiming each member
allocated a second copy. Move the name into the object and use the object's
archive name when setting as_needed.

Validation: all 27 native LTO tests pass. Workspace Clippy passes with
warnings as errors. All 545 upstream test files match C++ mold exactly.
2026-09-07 11:55:10 +09:00
Rui Ueyama bd95fc90ae Keep the shell test suite identical to C++ mold
Remove five Rust-only shell tests so the test cases and common support file
match the C++ suite exactly. The Cargo runner still provides test discovery and
execution.

Validation: compare every tracked test file and Git file mode against C++ mold
at 92eb17a07c60a88b3a1a8c9cef014ac84eaf0cf9; no missing, extra or modified test
cases.
2026-09-07 11:34:39 +09:00
Rui Ueyama 8d59a33536 Claim IR files for LTO in the command line order
Port C++ mold 92eb17a07c. Record IR files from
direct inputs, regular and thin archives, and linker scripts, then claim them
serially after sorting by their command line positions.

Remove the redundant claim mutex and once-only initialization guard. Require
mutable context access when claiming an IR file, and retain the simple
loaded-state check used by the serial plugin lifecycle.

Copy the upstream regression test unchanged. It reproduces an undefined
__cxx_global_var_init before the fix. All 27 native LTO tests pass afterward.
2026-09-07 11:34:39 +09:00
Rui Ueyama ee1a4c6cd5 Make linker-synthesized symbols local again unless exported
Port C++ mold 450832db6b. Apply the
internal-object binding rule after handling input-local symbols and relocatable
output, preserving global binding for exported synthetic symbols.

Copy the upstream symtab-binding test unchanged. It fails before the fix and
passes afterward. Native symbol-table, synthesized-symbol, start/stop, export
and relocatable-output checks: 24 passed, 1 skipped.
2026-09-07 11:30:30 +09:00
Rui Ueyama a01c78f771 Add the upstream data-limit regression test
Port the test from C++ mold d3cf4a23e0 unchanged.
The Rust symbol and section arenas were already replaced with vectors, so there
is no large writable arena reservation to change.

Validation: the native rlimit-data test passes with the current allocator.
2026-09-07 11:29:02 +09:00
Rui Ueyama b8876bc125 Skip an empty .sframe section
Port C++ mold 32626eba5d. Ignore empty SFrame
input before reading its header, as required for startup objects assembled by
GNU assembler 2.45.

Copy the upstream regression test unchanged. It failed with a corrupted-section
diagnostic before the fix and passes afterward. Native SFrame tests: 1 passed, 2
skipped because the assembler lacks SFrame v3 support.
2026-09-07 11:27:54 +09:00
Rui UeyamaandClaude Fable 5.1 92eb17a07c Claim IR files for LTO in the command line order
The parallel file reader handed IR files to the LTO plugin in
whatever order it found them. LLVM's LTO assumes that the module
holding the prevailing copy of a COMDAT group arrives before the
modules holding the other copies, as it does with a serial linker;
otherwise the losing copies' internal symbols, such as
__cxx_global_var_init.N, become undefined references in the LTO
result. The reader now only records IR files and claims them after
all input files have been found, sorted by command line position.
Claims were already serialized, so no parallelism is lost, and the
serializing mutex and once_flag in lto-unix.cc are no longer needed.

Fixes https://github.com/rui314/mold/issues/1649

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-07 09:35:25 +09:00
Rui UeyamaandClaude Fable 5.1 450832db6b Make linker-synthesized symbols local again unless exported
35f4a334 changed is_local() to derive a symbol's binding in .symtab from
its visibility. Linker-synthesized symbols never have their visibility
merged from the internal object's ELF symbols, so they went from
STB_LOCAL to STB_GLOBAL by accident. That adds _DYNAMIC, __bss_start,
_end and the like to the set of global symbols, which confuses ABI
checkers that read .symtab.

Restore the old rule for the internal object's symbols: they are local
unless we export them. The exported ones, such as __global_pointer$ on
RISC-V or __start_*/__stop_* under -z start-stop-visibility=protected,
stay global as before.

Reported by Yao Zi in https://github.com/rui314/mold/pull/1650

Fixes https://github.com/rui314/mold/issues/1651

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-06 14:55:13 +09:00
Rui Ueyama 68f9f33f3a Inline executable path lookups 2026-09-05 23:10:38 +09:00
Rui Ueyama 6bbfbcfc64 Use Origin directly instead of the OriginState alias 2026-09-05 22:46:48 +09:00
Rui Ueyama 66224d9ec2 Use memchr directly for symbol version separators 2026-09-05 22:46:28 +09:00
Rui Ueyama bc051b2e02 Use endian helpers directly in architecture backends 2026-09-05 22:43:59 +09:00
Rui Ueyama 620669c7a0 Use the standard library to find the executable path 2026-09-05 22:36:57 +09:00
Rui Ueyama c2fe4663d2 Simplify endian imports and remove unused SipHash aliases 2026-09-05 20:26:59 +09:00
Rui Ueyama fffec1424a Move endian utilities into a shared module
Keep Endian, byte order markers, and integer wrappers together in
util::endian. Add fixed-endian read/write helpers and use them from
Endian and architecture code.

Use one macro to provide new, get, and set consistently for all five
standard integer wrappers.
2026-09-05 20:15:39 +09:00
Rui Ueyama d3540dcab2 Simplify nested expressions and field access chains 2026-09-05 19:22:31 +09:00
Rui Ueyama 7cc7c4c99c Pass section headers to program header helpers
Make create_phdr's define and append helpers accept &ElfShdr directly.
Reuse section headers at their call sites and use short osec bindings
for optional sections.

Validation:
- cargo build --locked --workspace
- cargo test --locked --workspace: 5,487 integration tests passed,
  637 skipped, no failures; all 14 library tests passed. Four cross
  targets lacked a compiler or QEMU.
2026-09-05 18:06:12 +09:00
Rui Ueyama a6debf1ad3 Refactor 2026-09-05 17:53:48 +09:00
Rui Ueyama c8f06bb1a5 Rename output_chunks to chunks
Rename the module directory and update its declaration, imports, and
call sites to use crate::chunks.

Validation:
- cargo check --locked --workspace
- cargo test --locked --workspace: 5,487 integration tests passed,
  637 skipped, no failures; all library tests passed. Four cross targets
  were unavailable because their compiler or QEMU was missing.
2026-09-05 16:21:19 +09:00
Rui Ueyama 2eef5735f0 Flatten output chunk modules
Give each section a sibling module containing its type and functions,
and update callers to use the direct paths. Remove the misc and version
grouping modules and redundant nested wrappers.

Validation:
- cargo check --locked --workspace
- cargo test --locked --workspace: 5,488 integration tests passed,
  636 skipped, no failures; all library tests passed. Four cross targets
  were unavailable because their compiler or QEMU was missing.
2026-09-05 16:17:50 +09:00
Rui Ueyama 6fcdeb9088 Move map lookup and traversal to FrozenMap
ConcurrentMap exposed lookup and traversal while insertion was still
possible. Move those entry points to FrozenMap and keep the construction
API limited to allocation, insertion, prefetch, and freezing.

Freeze the GDB name map before collecting sorted entry references.
Extend the existing map test to verify lookup by the original entry IDs
after freezing, including the associated keys and values.
2026-09-05 15:38:47 +09:00
Rui Ueyama 01d51ca47c Define FrozenMap as a tuple struct
Replace FrozenMap's single named field with a private tuple field and
update freeze(), Default, and the forwarding methods to use it.
2026-09-05 15:24:11 +09:00
Rui Ueyama f9dd7ab7c6 Prefetch concurrent map buckets on more architectures
ConcurrentMap::prefetch only issued a cache hint on x86-64. Move the hint
into a shared utility and use stable inline assembly for x86, ARM32/64,
RISC-V32/64, PowerPC32/64, s390x, and LoongArch64.

Assume SSE on both x86 variants. Encode RISC-V PREFETCH.R as its ORI hint
so it remains a no-op without Zicbop. Keep unsupported architectures as
no-ops and preserve the map's empty-table guard.
2026-09-05 15:16:02 +09:00
Rui Ueyama 19416424bb Centralize transparent huge page advice
Move the Linux and Android MADV_HUGEPAGE calls into shared utilities,
with no-op implementations on other targets. Reuse them for output
mappings, concurrent-map storage, and symbol allocations, keeping the
symbol advice limited to complete pages within the allocation.

Extract platform-specific allocation and deallocation helpers for the
concurrent map, and guard page-alignment rounding against overflow.
2026-09-05 14:31:57 +09:00
Rui UeyamaandClaude Fable 5.1 d3cf4a23e0 Commit the arena's address space on demand
The 8 GiB arena was mapped read-write, so on hosts with overcommit disabled
(vm.overcommit_memory=2) Linux ignored MAP_NORESERVE and charged the whole
reservation against the commit limit, and fork() charged it again for the
child. Even a hello-world link then failed with "fork: Cannot allocate
memory". Reserve the range with PROT_NONE instead and make it writable in
64 MiB chunks as the bump pointer advances, as the Windows code already does
with MEM_COMMIT. Only the chunks in use are charged, by mmap and by fork()
alike, and transparent huge pages still back them.

Under RLIMIT_DATA, which counts writable private mappings the same way, a
trivial link now needs 0.5 GiB instead of more than 8 GiB.

Fixes https://github.com/rui314/mold/issues/1644

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-05 12:33:48 +09:00
Rui Ueyama 35e5d81cf1 Store input section IDs as separate file and index fields
InputSectionId stored an already packed integer while FragmentRef kept
its component indices. Store a private ObjId and zero-based u32 index in
InputSectionId, with direct field access in file() and index(). Confine
the packed representation to raw() and from_raw().

Keep the eight-byte size and the existing Origin bit layout. Use the
reserved u32::MAX index for NONE and apply the index-plus-one encoding
at the packing boundary so its payload remains zero. Extend the existing
round-trip test to cover zero indices, maximum supported indices, and
the placeholder value.
2026-09-05 09:42:38 +09:00
Rui Ueyama c4b3b1ff7c Move fragment reference encoding into FragmentRef
Origin was assembling and splitting FragmentRef's two indices itself,
while InputSectionId already supplied raw and from_raw methods. Add the
same encoding interface to FragmentRef and use it from Origin::new and
Origin::get.

Keep FragmentRef's typed fields and preserve the existing packed layout.
Origin still checks the 30-bit merged-section limit before shifting the
payload to make room for its tag.
2026-09-05 09:35:12 +09:00
Rui Ueyama f564f2e4b8 Reuse known file IDs when resolving input sections
ObjectFile section access and output-section construction already know
the owning file ID, but building a section ID reloads it from each input
section. Construct IDs from the known owner and the decoded dense index,
and pass the owner once into regular_ids_mut.

Move paired ID/reference lookup to ObjectFile so it resolves the ELF
section index once. Reuse the validated dense-index accessors for shared
and mutable section access, avoiding redundant owner loads and checks.
2026-09-04 22:42:47 +09:00
Rui Ueyama 05f4bb3c84 Skip bounds checks when resolving validated section IDs
InputSectionIds are assigned when a section is inserted into a file's
dense vector, and their file indices refer to the stable file pool.
Resolving these IDs currently repeats bounds checks for both indices on
hot section-access paths.

Add internal unchecked accessors for the pool and dense section vector,
and use them in Context::input_section. Document their insertion-based
preconditions and retain debug assertions on both bounds. Keep the
unsafe operations within these accessors and the ID resolution method.
2026-09-04 22:36:14 +09:00
Rui Ueyama 93b13c89eb Limit early fragment estimates to reachable CREL inputs
The early fragment estimate scans ordinary relocation tables before
archive extraction, including members that may never be selected. That
adds work to large links such as ClickHouse just to predict capacity.

Restrict the early estimate to cheap CREL header counts from direct
inputs and already reachable archive members. Leave ordinary relocation
counting to the existing preparation of selected files after COMDAT
selection. This retains the early reservation needed by CREL-heavy links
while avoiding an extra scan of ordinary relocation records.
2026-09-04 22:11:29 +09:00
Rui Ueyama 103c68bec5 Reserve fragment-symbol capacity before populating globals
Rewriting mergeable-section relocations can append millions of dummy
symbols after global symbols have been constructed. Reserving only local
symbols allows this later append to grow the vector and copy the already
populated table, which is particularly costly for CREL-heavy links.

Estimate fragment demand before gathering globals, using relocation
counts from CREL headers and inspecting ordinary relocations that refer
to mergeable section symbols. Include that estimate in the initial
reservation and compute it across files in parallel. Reapply huge-page
advice only when vector capacity actually changes.
2026-09-04 21:12:22 +09:00
Rui Ueyama 4a0485c774 Restore support for symbol names longer than 65535 bytes
The ClickHouse benchmark contains C++ symbol names longer than the u16
limit, causing valid input to panic in Symbol::new. Restore u32 storage
for the cached name length and retain the checked conversion into that
representation.

Change the boundary test to construct a 65536-byte name successfully and
verify that Symbol::name returns all of its bytes. This removes the
restriction introduced by narrowing the length field.
2026-09-04 20:58:33 +09:00
Rui Ueyama 182785e0c4 Update comments to describe vector and file-pool ownership
Replace obsolete arena terminology with the actual storage and lifetime
rules: symbols and section members live in vectors, input files belong
to a stable pool, and parallel construction reserves disjoint ranges
before exposing writable slots.

Remove stale ArenaPtr distance restrictions and describe background
debug-info work in terms of tasks and concurrency. These are comment
updates; allocation, ownership, and linker behavior are unchanged.
2026-09-04 19:00:56 +09:00
Rui Ueyama d7ee65d5d6 Retain huge pages and reduce vector growth
Reserve an upper bound for local symbols when gathering globals so that
appending locals does not immediately move a populated symbol vector.
Request transparent huge pages for whole pages inside that allocation,
leaving partial boundary pages that may contain allocator metadata
alone.

Let per-file section vectors grow with the inputs actually inserted;
reserving for every ELF header wastes space on relocation and metadata
headers that never become InputSections. Validate the packed file-index
limit when inserting files into the pool, and use debug assertions for
the redundant checks when encoding and decoding validated section IDs.
2026-09-04 18:58:19 +09:00
Rui Ueyama 6dd1c44ad9 Decode section table entries once per lookup
Section lookups were constructing an InputSectionId and decoding it
again even when the caller only needed a reference. Factor the ELF
section table decoding into input_index and use the resulting dense
index directly for shared, mutable, and paired ID/reference access.

Keep regular entries and entries with merge metadata on the same lookup
path. Extend the Origin round-trip test to cover the full supported
32-bit section-index range.
2026-09-04 18:26:26 +09:00
Rui Ueyama 046a28ce3d Access relaxation metadata through a mutable field reference
Have InputSectionExtra expose r_deltas_mut, returning a mutable
reference to the owned delta array. InputSection::set_r_deltas can then
perform the assignment itself while retaining its nonempty-array
assertion.

RISC-V and LoongArch return their embedded field. Targets without
relaxation metadata retain the unreachable guard for attempts to mutate
it, keeping the target-specific implementation limited to field access.
2026-09-04 18:23:06 +09:00
Rui Ueyama 34be96e62e Represent mergeable sections as fragment metadata
Rename MergeableSection to MergeInfo and update the section-table flags
and accessors to describe its role as metadata for an ordinary input
section. Update merging, symbol attachment, and debug-info callers to
use the new names.

Preserve the existing ownership model: eligible SHF_MERGE inputs remain
in their dense slots and are marked dead while their contents are
represented by fragments. Store merge metadata only for those inputs,
without adding a merge-info field to every InputSection.
2026-09-04 18:22:04 +09:00
Rui Ueyama b18be913d5 Replace the section arena with per-file vectors
Logical section IDs and directly owned target state allow input sections
to move when their storage grows. Store InputSection values in each
SectionList's dense vector and resolve IDs by indexing that vector. Keep
merged inputs in their slots so existing references remain valid.

Remove SectionArena, its thread-local allocation blocks, and the manual
section destruction code. Vector ownership now supplies allocation and
destruction, and the shared virtual-memory reservation module can be
deleted because neither symbols nor input sections use it anymore.
2026-09-04 18:19:08 +09:00
Rui Ueyama f477f0e277 Embed target-specific state in input sections
The self-relative pointer to InputSectionExtras prevents input sections
from moving and requires a separate arena allocation for target state.
Make InputSection generic over Arch and embed an associated
InputSectionExtra value directly in each section.

ARM32 stores its exception-index section link, and RISC-V and LoongArch
own their relaxation-delta arrays. Other targets use a zero-sized extra
type. Propagate the target type through section owners and callers, and
remove the self-relative pointer encoding and extras allocator.
2026-09-04 18:14:56 +09:00
Rui Ueyama 1b1c50a24f Decouple input section IDs from arena addresses
Arena-relative section IDs tie callers to the location of an allocation.
Represent InputSectionId as an object-file ID and a dense index within
that file, packed into 64 bits. Reserve two bits for Origin's tag and
keep zero available as the placeholder value.

Add a dense per-file slot table that still resolves to the existing
arena during this transition. Update symbol origins, output-section
members, relaxation, and layout lookups to resolve the logical ID
through its owner. This allows section storage to change independently
of the IDs used throughout the linker.
2026-09-04 18:04:09 +09:00
Rui Ueyama cc69390144 Replace the symbol arena with a vector
Symbols are already referenced through SymbolId, so their addresses only
need to remain stable while a construction or mutation phase borrows
them. Replace the 8 GiB SymbolArena reservation with Vec<Symbol> and let
the vector own and release the initialized symbols.

Keep the existing parallel construction scheme: reserve capacity before
publishing writable slots, assign disjoint ranges to workers, and set
the initialized length after they finish. Remove the custom virtual
memory allocation, slice access, and destruction code for symbols.
2026-09-04 17:58:31 +09:00
Rui Ueyama e70f3ca23e Limit symbol name lengths to 16 bits
Use u16 for Symbol::name_len, reducing the width of the cached length
field while keeping the existing thin pointer to the name bytes. This
representation imposes a maximum symbol name length of 65535 bytes.

Check the conversion in Symbol::new so that longer names produce an
explicit diagnostic. Add a regression test for a 65536-byte name to
exercise that boundary.
2026-09-03 21:45:16 +09:00
Rui Ueyama d3e688cd83 Use arena IDs for symbol input sections 2026-09-03 21:37:37 +09:00
Rui Ueyama e9f327a938 Store input-section origins by index 2026-09-03 18:47:26 +09:00
Rui Ueyama 417d17cbea Simplify origin value types 2026-09-03 16:22:39 +09:00
Rui Ueyama 4f20b0e648 Unify symbol origin encoding 2026-09-03 16:16:27 +09:00
Rui Ueyama 3d0fa5d93c Refactor symbol origin decoding 2026-09-03 16:09:29 +09:00
Rui Ueyama ee48017b66 Trim unnecessary inline annotations
Let the compiler decide whether to inline private and generic helpers that do
not correspond to explicitly inline C++ code. Keep forced inlining on the hot
ELF and relocation paths identified by the C++ implementation and perf
profiles.

Firefox-debug performance remained neutral in both single-threaded and
32-thread runs. The full test suite and release clippy checks pass.
2026-09-03 15:57:45 +09:00
Rui Ueyama 1ce76ea568 Clean up translated comments
Merge duplicated C++ and Rust comment blocks into single explanations that
describe the Rust implementation while retaining the original rationale.
Remove stale C++ type references and restore the missing rustdoc summary for
sorted concurrent-map entries.
2026-09-02 21:39:33 +09:00
Rui Ueyama 3396be62b4 Remove unused linker APIs
Remove helpers, accessors, and constants that are unreachable from the mold
command and its tests. This includes wrapper methods whose only callers were
other dead wrappers, unused ELF integer operations, and unused concurrent-map
APIs.

Keep the remaining internal len methods crate-visible so Clippy does not treat
them as incomplete public collection APIs.
2026-09-02 21:29:43 +09:00
Rui Ueyama 7a340c6469 Store origin tags in the origin word
Keep Origin eight bytes and use its low two bits to distinguish input
sections, output chunks, merged-section fragments, and symbols. Remove the
separate origin-kind bits from Symbol’s visibility byte.

This follows C++ mold’s tagged-pointer representation without the auxiliary
tables and per-variant codecs required by the discarded 32-bit experiment.
2026-09-02 20:47:56 +09:00
Rui Ueyama f6f2e909a6 Revert "Pack symbol origins into 32 bits"
The experiment reduced Symbol from 48 to 40 bytes, but it required a
manual codec for every ChunkId variant, a separate namespace for allocated
merged sections, and context-dependent origin decoding. It added 434 net
lines.

It also made the previous Rust linker about 2% slower on Firefox debug and
10% slower on TensorFlow debug, even though it remained faster than C++ mold.
Prefer the simpler 64-bit origin until the memory saving justifies those
costs.
2026-09-02 20:07:33 +09:00
Rui Ueyama 68f8cbc3d6 Pack symbol origins into 32 bits
Use unary low-bit tags so fragment origins have a 31-bit payload. Pack
allocated merged-section IDs and fragment bucket IDs into that payload,
and use compact encodings for input sections, chunks, and symbols.

Also compact the remaining symbol metadata so that Symbol shrinks from 48
to 40 bytes.
2026-09-02 20:07:10 +09:00
Rui Ueyama 94e2104d70 Remove temporary thunk address vectors
Split the output-section and symbol-table borrows so
gather_thunk_addresses can record addresses while traversing thunks. This
matches the C++ loop and avoids a temporary allocation for each section.
2026-09-02 17:44:14 +09:00
Rui Ueyama 4322dad7c2 Inline stderr terminal checks
Call IsTerminal directly at the two sites that select automatic diagnostic
coloring, and remove the one-line helper.
2026-09-02 17:32:30 +09:00
Rui Ueyama de1123d600 Make the archive member parser an iterator
Replace the callback-based internal parser with an Iterator built using
std::iter::from_fn. Keep the eager Vec results and member creation order
unchanged.
2026-09-02 17:25:02 +09:00
Rui Ueyama a37ae7e290 Preserve file-open errors
Return the original io::Error from the shared file-opening path instead of
discarding it and reading errno later. Keep optional file lookup semantics by
mapping NotFound to None, and remove errno_string.
2026-09-02 17:12:51 +09:00
Rui Ueyama 20d42d3a31 Update C++-specific comments for Rust
Rewrite comments copied verbatim from C++ so they describe the Rust
implementation and terminology. Remove notes that have no Rust counterpart,
and update source-file references to their Rust names.

Keep C++ comparisons when they document an intentional implementation
difference or a remaining performance gap.
2026-09-02 16:38:15 +09:00
Rui UeyamaandClaude Fable 5 32626eba5d Skip an empty .sframe section
GNU assembler 2.45 emits a zero-size .sframe section for an input file
that needs no unwind info, such as glibc's Scrt1.o on Ubuntu 25.10.
parse_sframe() read a header past the end of such a section and reported
it as corrupted.

Fixes https://github.com/rui314/mold/issues/1646

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 14:36:22 +09:00
Rui Ueyama 2e731001d9 Make diagnostics process-wide
Mold performs one link per process, so keep diagnostic settings, error state,
and output serialization in a process-wide singleton. This removes
diagnostic plumbing from contexts and helper APIs while preserving the mutex
that keeps messages from worker threads from interleaving.
2026-09-02 12:27:24 +09:00
Rui Ueyama c433f96074 Move SipHash into utility module
Restore the configurable C++ implementation with const-generic round
counts and output width. Keep ICF-specific digest conversion in icf.rs.
2026-09-01 20:59:50 +09:00
Rui Ueyama 36ad403f7c Add Linux distribution script
Port mold dist.sh to build Rust release binaries in old distro
containers. Download a pinned official Rust toolchain as gzip, fetch
Cargo.lock dependencies before disabling the network, and normalize
package timestamps.

Allow install-mold.sh to consume an external Cargo target directory.
2026-09-01 20:39:34 +09:00
Rui UeyamaandClaude Fable 5 b8ba529e57 Fix path_clean dropping leading .. components
path_clean("../../a/b") returned "a/b": the ParentDir branch popped a
trailing ".." from the output instead of appending another one, so any
path with two or more leading ".." components collapsed into a wrong
path. C++ mold uses std::filesystem::path::lexically_normal, which
keeps leading ".." components intact.

Library search paths are cleaned with this function, so mold failed
with "library not found" whenever a Makefile referenced an in-tree
library as -L../../<dir>, which is a very common pattern. Found by the
Gentoo all-package campaign within its first hour: sys-fs/lvm2,
sys-fs/xfsprogs, sci-astronomy/montage, dev-libs/OpenNI2,
dev-erlang/fast_yaml, and app-backup/spideroak-bin all failed with
mold-rust and built with mold-c++.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0118ERbwNepcG1Z9gh5cm6Ut
2026-09-01 18:49:49 +09:00
Rui UeyamaandClaude Fable 5 3881cd2a69 Remove manpage update workflow
Benchmark record, 2026-09-01 (unrelated to this change; this tree is
what was measured, built as 6becb4da before this message was amended):

C++ reference: mold at e322a3cc, i.e. the translation base fbaaa75f
plus 15 fixes that do not touch hot paths.

Protocol: ~/bench/tab4-bench.sh as-is — run 1 warms the page cache,
median of runs 2-6 reported; all runs of one linker complete before
the other linker starts; sync + dirty-writeback wait between runs;
--no-fork; default thread count (min(ncores, 32) = 32 on this
128-core machine); frequency boost off, performance governor,
THP madvise, idle machine.

Workload                    C++ (s)  Rust (s)  Rust vs C++
blender-debug                  0.94      0.82       -12.8%
blender-release                0.20      0.17       -15.0%
chrome-debug-arm64             1.88      1.97        +4.8%
chrome-debug-x86_64            1.75      1.80        +2.9%
chrome-release-arm64           0.77      0.79        +2.6%
chrome-release-x86_64          0.66      0.69        +4.5%
clang-debug                    1.49      1.28       -14.1%
clang-release                  0.11      0.10        -9.1%
clickhouse-debug               1.14      1.09        -4.4%
clickhouse-release             0.43      0.43        +0.0%
firefox-debug-arm64            1.07      0.98        -8.4%
firefox-debug                  0.87      0.87        +0.0%
firefox-release                0.21      0.20        -4.8%
godot-debug                    0.51      0.47        -7.8%
godot-release                  0.08      0.08        +0.0%
libmergedlo.so-debug           0.46      0.47        +2.2%
libmergedlo.so-release         0.20      0.18       -10.0%
pytorch-debug                  0.93      0.84        -9.7%
pytorch-release                0.15      0.14        -6.7%
tensorflow-debug               3.16      2.89        -8.5%
tensorflow-release             0.69      0.63        -8.7%

Geomean -5.1% (Rust faster); 13 faster, 3 even, 5 slower. No workload
regressed against the import-time table in the initial commit; the
open item remains chrome's +2-5%.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0118ERbwNepcG1Z9gh5cm6Ut
2026-09-01 18:03:03 +09:00
Rui Ueyama 6ce8c8fcda Keep imported mold tests unmodified 2026-09-01 15:25:22 +09:00
Rui Ueyama 958bce3d88 Update generated manual for arm64 rewrite-endbr 2026-09-01 15:22:05 +09:00
Rui Ueyama 4959c02bd6 [LTO] Document linker version string lifetime 2026-09-01 15:21:13 +09:00
Rui Ueyama da5cc1049c Fix thin archives with a __.SYMDEF member 2026-09-01 15:19:46 +09:00
Rui Ueyama 5768f27eef Report malformed hexadecimal --defsym values 2026-09-01 15:17:54 +09:00
Rui Ueyama 17214ae348 Add mergeable weak-definition regression test 2026-09-01 15:16:14 +09:00
Rui Ueyama a98878f988 Add PT_NOTE section-order regression test 2026-09-01 15:14:35 +09:00
Rui Ueyama 414976992d Add mixed DWARF merged-string regression test 2026-09-01 15:13:06 +09:00
Rui Ueyama d4963fbc38 Add nonalloc relocation range regression test 2026-09-01 15:11:41 +09:00
Rui Ueyama 5075908706 Recognize GCC LTO objects with 65280 or more sections 2026-09-01 15:10:24 +09:00
Rui Ueyama ee7446a9d7 Support -z rewrite-endbr on arm64 2026-09-01 15:07:49 +09:00
Rui Ueyama 046ca4fa79 Remove unused function 2026-09-01 14:46:04 +09:00
Rui Ueyama 19dce43b67 Remove unused linker code
Drop the unused x86-64 relocation slice, obsolete FDE boundary variable and
unreferenced page-alignment helper.
2026-09-01 14:10:18 +09:00
Rui Ueyama 3dc5e88f6d Use an enumerated RISC-V relocation scan
Keep the allocation-free forward search while iterating over relocation
records directly, as conventional Rust and clippy expect.
2026-09-01 14:10:18 +09:00
Rui Ueyama 2cf15f20f0 Parallelize section shrinking updates
Apply relocation deltas, adjust symbols and recompute executable output
section layouts in parallel, matching the three file and chunk parallel
loops in C++ mold.
2026-09-01 14:10:18 +09:00
Rui Ueyama 19d8d87797 Parallelize ARM BE8 code-byte swapping
Process object files in parallel as C++ mold does. Use the existing
disjoint-range output-buffer abstraction so each worker can reverse its
input sections in place.
2026-09-01 14:10:18 +09:00
Rui Ueyama f55084e9dc Parallelize PPC64v1 OPD rewriting
Process object files in parallel as C++ mold does. Serialize symbol access
with the existing byte-sized symbol lock and rewrite each file's relocation
records in place.
2026-09-01 14:10:17 +09:00
Rui Ueyama 273693951d Avoid allocating RISC-V relocation iterators
Use the same direct backward or forward linear search as C++ mold when
finding the HI20 relocation paired with a LO12 relocation.
2026-09-01 14:10:17 +09:00
Rui Ueyama a2b72c33f8 Match mold's zlib sync-flush workaround
Use stock zlib's low-level API to reproduce the C++ Z_BLOCK, deflatePending,
optional deflatePrime and Z_SYNC_FLUSH sequence. This avoids the old
libbacktrace boundary bug and preserves C++ mold's compressed bytes.
2026-09-01 14:10:17 +09:00
Rui Ueyama d29ee0c4c5 Restrict disk-full diagnostics to the output mapping
Install the Unix handlers with SA_SIGINFO and track the mapped output range
as it is created, extended or moved. Report a possible full disk only when
SIGBUS or SIGSEGV identifies an address in that range.
2026-09-01 14:10:17 +09:00
Rui Ueyama 344773166c Implement mold statistics counters
Port the C++ Counter facility and collect the same parser, section, symbol,
ICF, relocation, COMDAT, thunk and merged-section statistics. Keep counter
updates dormant unless --stats is requested.
2026-09-01 14:10:17 +09:00
Rui Ueyama 4bffbbd530 Include the git commit hash in the mold version 2026-09-01 14:10:17 +09:00
Rui Ueyama b8d9d12619 Apply diagnostic options while parsing arguments 2026-09-01 14:10:17 +09:00
Rui Ueyama 8ab361da56 Implement the MOLD_JOBS process gate 2026-09-01 14:10:17 +09:00
Rui Ueyama e8ab792f46 Track all mapped files for repro and dependencies 2026-09-01 14:10:17 +09:00
Rui Ueyama 879e9bed13 Preserve RISC-V attributes in relocatable output 2026-09-01 14:10:17 +09:00
Rui Ueyama d0aecfd12a Drop symbols belonging to all dead input sections 2026-09-01 14:10:17 +09:00
Rui Ueyama 5dcfcbedb2 Assign thunk names in deterministic output order 2026-09-01 14:10:17 +09:00
Rui Ueyama 43964392e1 Match C++ mold file priorities for LTO output 2026-09-01 14:10:17 +09:00
Rui Ueyama 5051b051be Fix s390x GOTENT allocation during relocation scan 2026-09-01 14:10:17 +09:00
Rui UeyamaandClaude Fable 5 adb411d475 [LTO] Don't return a dangling pointer as the linker version string
get_api_version handed the plugin the buffer of a stack-local
std::string, which dies when the function returns. Make it static.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 878599b795 Fix an infinite loop on a thin archive with a __.SYMDEF member
The thin archive reader skipped a __.SYMDEF symbol table without
advancing the read position, so it kept parsing the same member header
forever. Skip past the member contents like the other symbol table
branch does.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 4ebc916bc9 Add missing relocation names to rel_to_string
R_RISCV_TLSDESC, R_RISCV_GOT32_PCREL, R_SPARC_SIZE32 and R_SPARC_JMP_IREL
were printed as "unknown (n)" in diagnostics.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 a129618d11 Report an error for a malformed hexadecimal --defsym value
parse_defsym_value returned a default-constructed variant on a bad hex
string such as 0xz, which holds a null Symbol pointer. The null pointer
was then dereferenced when marking GC roots, crashing the linker instead
of printing a diagnostic.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 a9af11f74a Don't attach section pieces to symbols resolved to other files
reattach_section_pieces rewrote every symbol whose local definition is in
a mergeable section, including global symbols that lost resolution to a
definition in another file. The loser then redirected the symbol to its
own section piece. Since the pass runs on all files in parallel, it was
also a data race on the shared Symbol.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 d55b408ff0 [s390x] Fix byte order of 12-bit displacement fields
R_390_12, R_390_PC12DBL, R_390_PLT12DBL, R_390_GOT12 and R_390_GOTPLT12
OR'd their 12-bit value into the instruction through a little-endian
16-bit accessor. s390x is big-endian, so the value landed in the wrong
bytes, corrupting both the displacement and the base register field.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 f3b74cbb6a Fix PT_NOTE creation to consult the right chunk vector
create_phdrs walks a filtered, address-sorted copy of ctx.chunks, but the
loop that merges adjacent note sections into one PT_NOTE examined
ctx.chunks with an index into that copy. The two vectors differ e.g. when
the ELF and program headers are not memory-mapped, in which case every
note section got its own PT_NOTE segment.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 1f56c36413 Fix string overlap in huge merged sections with mixed DWARF32/64
When a merged section is laid out in two regions, 32-bit-addressable
fragments followed by the rest, the loop that rebases per-shard offsets to
section-global ones started at shard 1. That is fine for the 32-bit region,
whose shard 0 needs no adjustment, but it left shard 0 of the 64-bit region
unadjusted, so its fragments overlapped the beginning of the section. Start
at shard 0; the 32-bit fragments of that shard just get zero added.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 e2a28d61eb [RISC-V] Fix R_RISCV_ADD8 and R_RISCV_SUB8 in allocated sections
The alloc relocation path incremented the local pointer loc instead of the
byte it points to, so the relocated byte was left untouched. The result was
wrong whenever an 8-bit label difference spanned code whose size changed
during relaxation. The non-alloc path already did this correctly.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:49:46 +09:00
Rui UeyamaandClaude Fable 5 19e5360530 Fix swapped arguments to the relocation range checker
The non-alloc relocation paths of i386, ARM64, s390x, SPARC64 and PPC64V1
passed the relocation value and the relocation index to check_range in the
wrong order. The intended range check was thus never performed, and if the
relocation index happened to fall outside the value range, the error path
indexed the relocation table with the relocation value, reading out of
bounds.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 13:26:59 +09:00
Rui Ueyama fed11f6eca Fix FreeBSD -run and serialize sanitizer tests
Use the same KERN_PROC_PATHNAME sysctl as C++ mold when locating the
current executable on FreeBSD, where /proc may not be mounted. Run one
shell test at a time under sanitizers so large cases do not overlap,
while leaving two CPUs available to the linker under TSan.
2026-09-01 12:43:09 +09:00
Rui Ueyama 13757779c5 Copy the man-page update workflow 2026-09-01 12:25:51 +09:00
Rui Ueyama 063a8fda2a Limit CI builds to required mold targets
Use Cargo per-target features in native and platform jobs so they do not
rebuild all 20 linker instantiations. Tests that require an i386 linker
already skip when that target is disabled. Keep test-all-targets using
defaults for complete QEMU coverage.
2026-09-01 12:13:35 +09:00
Rui UeyamaandClaude Fable 5 f68cb6fb18 Recognize GCC LTO objects with 65280 or more sections
If an object file has SHN_LORESERVE (0xff00) or more sections, e_shnum
is zero and the actual number of sections is stored in the first section
header's sh_size field. is_gcc_lto_obj() used the raw e_shnum, so it
scanned no sections and misclassified such LTO objects as regular
object files.

Fixes https://github.com/rui314/mold/issues/1642

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-01 12:12:10 +09:00
Rui Ueyama 6e5fa07222 wip 2026-09-01 11:56:49 +09:00
Rui Ueyama a53b8db45b Fix sanitizer and FreeBSD CI failures
Give ThreadSanitizer extra time for the many-output-sections test.

Disable incremental artifacts and debug info in the disk-constrained FreeBSD VM.

Also suppress the non-x86 unused prefetch parameter warning.
2026-09-01 11:06:43 +09:00
Rui Ueyama 33261b57c8 Install Git in MSYS CI 2026-09-01 10:34:53 +09:00
Rui Ueyama 890313dd9c Use Git CLI for Cargo in MSYS CI
MSYS Cargo's bundled libgit2 rejects GitHub's certificate. Use the Git CLI,
which reads the certificate setup provided by MSYS2.
2026-09-01 10:29:15 +09:00
Rui Ueyama dceb9bbb4b Port macOS, Windows, MSYS2 and FreeBSD CI jobs
Add the four platform jobs from C++ mold and make the Rust implementation
compile on each native platform.

Use the corresponding Windows virtual-memory, output-file, subprocess, LTO
and process-timer facilities. Keep the Unix paths for macOS and FreeBSD while
applying platform-specific system calls only where C++ mold does.

Tested with cargo build --release, cargo test and cargo clippy --release
--all-targets. The MinGW executable also links against dlfcn-win32.
2026-09-01 10:25:19 +09:00
Rui Ueyama 6d9d6fe801 Discard successful test work directories
The all-target CI job exhausted its runner disk because it retained the
output of every completed shell test. Remove each work directory after a
passing or skipped test while preserving logs and all failed-test files for
diagnosis.
2026-09-01 00:06:21 +09:00
Rui Ueyama 305fddf397 Reduce all-target CI artifacts
The multi-architecture build generated incremental and debug artifacts on
an ephemeral runner, leaving too little room for the test outputs and
causing the one-hour timeout. Disable both for this job.

In Ubuntu 24.04 Podman, the clean build used 693 MB and the completed test
tree used 7.4 GB. All targets passed with pass=6782 skip=895 fail=0.
2026-08-31 23:24:56 +09:00
Rui Ueyama bffdaa5b1f Fix big-endian ARM synthesized literals
ARM BE8 instructions are little-endian, but data embedded in PLT and
thunk entries uses target byte order. Match C++ mold's ul32 instruction
templates and U32<E> literal stores.

CI-equivalent Ubuntu 24.04 Podman:

  all targets: pass=6782 skip=895 fail=0
  armeb: pass=368 skip=53 fail=0
2026-08-31 23:15:01 +09:00
Rui Ueyama b6603f2d3c Keep CI commands on one line
Write each Cargo invocation on one line instead of using shell continuation
characters.
2026-08-31 21:45:03 +09:00
Rui Ueyama 9192ec01bc Limit CI to test-bearing targets
All workspace crates are built before testing, but the target crates and CLI
binary contain no unit tests. Select the library and integration test targets
so Cargo does not exhaust the runner disk linking empty harnesses.
2026-08-31 21:42:47 +09:00
Rui Ueyama c472dc5b34 Fix output preallocation on musl
musl uses an unsigned type for statfs.f_type while libc exposes
TMPFS_MAGIC as a signed value. Convert the constant to the field's type so
the comparison builds with both musl and glibc.

Match C++ mold by attempting fallocate when fstatfs fails.
2026-08-31 21:18:54 +09:00
Rui Ueyama c1ce9ebb75 Configure PATH in distro containers
The job-level environment did not populate the container configuration, so
the Rust toolchain action still lost the system directories when it extended
GITHUB_PATH. Set PATH through container.env instead.
2026-08-31 21:09:08 +09:00
Rui Ueyama c6393dfd84 Fix PATH setup for Tumbleweed CI
The Tumbleweed image does not declare PATH in its container
configuration. GitHub's implicit default disappears when rust-toolchain
adds Cargo's bin directory through GITHUB_PATH. Define the normal system
path explicitly for the distro matrix so Bash remains available.
2026-08-31 21:04:22 +09:00
Rui Ueyama 678d194a68 Let the dependency installer bootstrap Bash
Run install-test-deps.sh with /bin/sh so minimal container images can
invoke it. Expand the Debian cross-toolchain package list without Bash
brace expansion, install Bash on Tumbleweed, and remove the Alpine
bootstrap step from CI.
2026-08-31 20:51:43 +09:00
Rui Ueyama df81f7363c Sync Gentoo repository before installing dependencies
Retain the repository synchronization performed by the original mold
script before asking Portage to install curl.
2026-08-31 20:46:32 +09:00
Rui Ueyama 8857b8796d Install Bash before Alpine tests
The Alpine base image does not include Bash, so install it before
running install-test-deps.sh. Keep the dependency script Bash-based so it
can use brace expansion.
2026-08-31 20:38:33 +09:00
Rui Ueyama d77d5e36d9 Add GitHub Actions test coverage
Run the complete multi-architecture suite and native tests across the
same Linux distribution matrix as C++ mold. Install the extra toolchains
used by the upstream tests and fail CI if any expected target is
unavailable.

Build and test ASan and TSan configurations with nightly Rust. Use the
system allocator for sanitizer builds and instrument the two C adapters as
well. Limit TSan to two CPUs to avoid false reports from Rayon while still
exercising parallel execution.

Make install-test-deps.sh portable across the distribution containers.

Tested with cargo test: pass=5360 skip=636 fail=0. ASan passed 477
native tests with 9 skips, and TSan passed 454 native tests with 32 skips.
2026-08-31 20:32:33 +09:00
Rui Ueyama 2a2453f93d Rewrite emitted relocations in place
Match C++ mold's relaxation behavior by updating input relocation records
at the point where instructions are rewritten. Store decoded CREL records
in typed mutable side tables so emitted relocation sections observe the
same updates, and remove the relocation type reconstruction hooks.
2026-08-31 18:57:06 +09:00
Rui Ueyama 8c837eef01 wip 2026-08-31 18:26:48 +09:00
Rui Ueyama 21787c1417 Remove synthetic ELF layouts
Use I386, X86_64, M68k, and Sparc64 as representative layouts when
classifying GCC LTO objects, matching C++ mold. Use real target layouts for
the compile-time record assertions and remove Elf32Le, Elf64Le, Elf32Be, and
Elf64Be.

Also remove the obsolete Record trait. Direct file records use
FileRecord, and Record had no implementations or callers.

Checked with cargo check --workspace. The test suite was not rerun as
requested.
2026-08-31 17:49:23 +09:00
Rui Ueyama 63452ee050 Simplify ELF layout metadata
Rename ProgramHeaderRecord and CompressionHeaderRecord to the shorter
PhdrRecord and ChdrRecord names. Expand the four plain layout definitions
directly and remove the plain_layout macro.

Infer the ELF class from the layout word size and whether relocations
carry addends from RelRecord. This removes the duplicated IS_64 and IS_RELA
declarations from every target.

Tests: cargo test (pass=5360 skip=636 fail=0)
Lint: cargo clippy --release --all-targets
2026-08-31 17:41:22 +09:00
Rui Ueyama bfff76057f Map ELF tables directly onto file records
Keep input section headers and symbols in their endian-aware on-file
representation instead of decoding them into target-independent mirrors.
Carry that representation through output chunks and expose fields through
record traits, so reads and writes remain target-endian.

Remove SymbolEntry, SectionHeader, SymTable, ShdrTable, and RecordLayout
now that no converted record mirrors remain. Move the SFrame constants to
elf/consts.rs.

On the 21-workload, 32-thread benchmark, the geometric-mean time changed
by -1.1% versus 801fc. Excluding tensorflow-debug's unusually large
improvement, it was effectively flat at -0.1%.

Tests: cargo test (pass=5360 skip=636 fail=0)
Lint: cargo clippy --release --all-targets
2026-08-31 17:25:36 +09:00
Rui Ueyama 80c1b9c258 Use endian-aware ELF records
Represent uniform ELF records with public target-endian fields and select
class-dependent symbol, program-header, and compression-header layouts
through Layout. Convert differing layouts as complete records instead of
adding field-by-field forwarding accessors.

Remove the raw st_info API and the old offset-based record readers.

Tests: cargo test
Tests: cargo clippy --release --all-targets
2026-08-31 15:05:16 +09:00
Rui Ueyama 801fc08a37 wip 2026-08-31 14:44:09 +09:00
Rui Ueyama cd04a2f32d Use target-specific relocation records
Define byte-backed integer and relocation types that mirror the C++ ElfRel
specializations, including the SPARC64 type-data field.

Select each relocation record through Layout and view input and output
tables as ordinary typed slices. This removes the host-native conversion
wrappers and the synthetic r_info representation.

Preserve target byte order independently of the host.

Tests: cargo test
Tests: cargo clippy --release --all-targets
2026-08-31 14:14:12 +09:00
Rui Ueyama dc15979a21 Generate relocation names from constants
Define relocation constants and diagnostic-name functions for each
architecture from a single macro invocation. Keep each macro input to the
relocation name and value, and use stringify! to produce the diagnostic
spelling.

Remove the separate relnames module so relocation names and values can no
longer drift apart.

Validation passes with cargo build --release and cargo clippy --release
--all-targets. Cargo test reports 5360 passed, 636 skipped, and no failures.
2026-08-31 12:52:52 +09:00
Rui Ueyama c5f5720a7c Make relocation traversal iterable
Replace the callback-only relocation traversal API with streaming Rust
iterators. Make CrelReader and RelocationIter implement Iterator while
preserving deferred CREL decoding without allocation.

Use ordinary for loops in the architecture backends and gdb-index reader.
Keep a specialized fold implementation so Iterator::for_each can select the
ordinary or CREL representation once.

The full test suite passes with 5360 cases passed, 636 skipped, and no
failures. Clippy is clean.

Five-run firefox-debug medians improved from 0.798 s to 0.765 s at 32
threads and from 11.135 s to 11.002 s with one thread. The C++ reference
at fbaaa75f measured 0.792 s and 11.999 s, respectively.
2026-08-31 12:41:09 +09:00
Rui Ueyama a91d19d5e5 Use the global Rayon thread pool
Configure Rayon's process-global pool after target detection and process
forking. Register the main thread as worker zero so --threads=N limits the
linker to N active threads.

Run the linker directly instead of wrapping its entire body in
ThreadPool::install. This follows mold's command-oriented execution model
and its process-wide TBB parallelism limit.
2026-08-31 11:48:12 +09:00
Rui Ueyama a1b835bd5e Align Rust source names with mold
Rename Rust modules to match their C++ source counterparts where
practical. Use archive_file, cmdline, error, output_chunks,
shrink_sections, and perf while keeping mapped_file, output_file, lto,
and subprocess unchanged.

Remove filename-only comments added during the port. They were not copied
documentation and became redundant once the filenames were aligned.

Update all module paths and target entry points for the renamed modules.
2026-08-31 11:35:52 +09:00
Rui Ueyama defabc0672 Reduce the shell-test timeout
Limit each shell test to 30 seconds instead of 300 so a hung compiler or
QEMU process fails promptly.
2026-08-31 11:07:01 +09:00
Rui Ueyama 25433d61ed Add test dependency installer
Carry over mold's distribution-specific dependency installer as
install-test-deps.sh. Drop CMake packages that the Rust build no longer uses.

Keep test setup out of the user build instructions. Explain the stable Rust
and native C compiler requirements directly in README.
2026-08-31 10:59:36 +09:00
Rui Ueyama 39b103e44e Update user documentation and add installer
Copy the current README, manual, logos, and license from mold. Adapt the
build and installation instructions for the Rust implementation.

Add install-mold.sh to install the binary, preload library, manual, license,
and linker symlinks under a configurable PREFIX with DESTDIR support.
2026-08-31 10:41:29 +09:00
Rui Ueyama 91164c701b Rename csrc to c
Move the two remaining C source files from csrc/ to c/ and update the
build script and LTO documentation to use the new paths. The files are
otherwise byte-identical.

Tests: cargo build --release
Tests: cargo test (pass=5360 skip=636 fail=0)
Tests: cargo clippy --release --all-targets
Tests: cargo fmt --all -- --check
2026-08-31 10:05:17 +09:00
Rui Ueyama fbecc040bb Use the pinned mimalloc Rust crate
Replace the local allocator wrapper and vendored mimalloc sources with the
rui314/mimalloc_rust repository pinned at:

cc58b72775b8b5bc89ee37752af61fcb4f7c2909

The dependency contains mimalloc 3.5.0 and its overlapping upstream files
are byte-identical to the deleted copy. Update the workspace, allocator
import, lockfile, and README.

Tests: cargo build --release
Tests: cargo test (pass=5360 skip=636 fail=0)
Tests: cargo clippy --release --all-targets
Tests: cargo fmt --all -- --check
2026-08-31 09:52:51 +09:00
Rui Ueyama 197c1e8cb9 Run the shell suite through cargo test
Register a custom integration-test target for mold-cli and move the Rust
runner into the mold-tests workspace crate. Keep all 537 shell-test files
byte-for-byte unchanged under tests/cases, run every available target and
QEMU job by default, and stage Cargo-built test artifacts in the active
profile directory.

Remove run-tests.sh and make every workspace crate a default member so
plain cargo test is the complete test entry point.

Use explicit wrapping arithmetic for unsigned C++ operations exposed by
the debug-profile test suite.

Tested with:
  cargo test
  cargo build --release
  cargo clippy --release --all-targets
  cargo fmt --all -- --check

Full shell-suite result: 5,996 scheduled, pass=5360, skip=636, fail=0.
2026-08-31 09:24:02 +09:00
Rui Ueyama 4fbfe4cba7 Ignore local benchmark and backup files
Keep the externally managed bench directory and editor backup files out of
the mold-rust working tree.
2026-08-31 08:02:47 +09:00
Rui Ueyama e0cb69af5d Preserve mold's comments in the Rust port
Treat every C++ implementation comment at mold commit
fbaaa75f9f as a first-class porting
asset. Restore all 2,179 non-namespace comment occurrences at their
Rust counterparts, including file-level explanations, ABI notes,
examples, TODOs, original typos, inline instruction annotations, and
repeated comments. Add provenance where one Rust implementation
consolidates multiple C++ implementations. Closing namespace labels
are the sole comments without a Rust construct.

Verify that all 537 upstream test files remain byte-for-byte identical
to the pinned C++ tree.

The audit also exposed a few implementation-fidelity gaps: reopen
input mappings before writing a repro archive, reset discarded-COMDAT
symbol indices after owner selection, mirror C++ GOT
dynamic-relocation accounting branches, and reserve version-section
output storage as C++ does. These are faithful translations, not new
algorithms.

Validated with cargo build --release, ./run-tests.sh --all (pass=5360
skip=636 fail=0), cargo clippy --release --all-targets, cargo fmt
--check, and git diff --check.
2026-08-31 08:01:19 +09:00
Rui Ueyama 2fee580c65 Use stable map entries for GDB index construction
Mirror C++ ConcurrentMap sorted-entry handling: reserve each shard vector, use unstable sorting, retain direct stable entry references, and test occupancy through the atomic key pointer alone.

Store direct stable NameEntry references in indexed-name records so the table writer does not translate a bucket ID for every public-name occurrence.

Reference C++ mold: fbaaa75f9f

Validation:
- cargo build --release
- ./run-tests.sh --all: pass=5360 skip=636 fail=0
- cargo clippy --release --all-targets

32-thread benchmark, median of five, with sync before and sync plus sleep 1 after every linker invocation:
- chrome-debug-arm64: C++ 1.736 s, Rust 1.834 s (+5.65%)
- chrome-debug-arm64 --no-gdb-index: C++ 1.576 s, Rust 1.592 s (+1.02%)
- chrome-release-arm64: C++ 0.715 s, Rust 0.738 s (+3.22%)

The remaining Chrome debug difference is isolated to TBB low-priority arena behavior; Rayon has no corresponding task-priority feature.
2026-08-30 23:49:29 +09:00
Rui Ueyama d879a504e7 Keep input files in stable owning pools
Mirror mold obj_pool/dso_pool ownership plus live pointer vectors so removing unreachable files does not move or renumber the underlying objects.

Reference C++ mold: fbaaa75f9f

Benchmark: 32 threads, median of five measured runs after one C++ warmup. C++ and Rust runs were interleaved; sync was run before, and sync plus sleep 1 after, every linker invocation.

workload                     C++ (s)  Rust (s)  Rust vs C++
blender-debug                  0.842     0.767       -8.91%
blender-release                0.190     0.165      -13.16%
chrome-debug-arm64             1.709     1.788       +4.62%
chrome-debug-x86_64            1.594     1.614       +1.25%
chrome-release-arm64           0.714     0.745       +4.34%
chrome-release-x86_64          0.608     0.625       +2.80%
clang-debug                    1.301     1.142      -12.22%
clang-release                  0.098     0.096       -2.04%
clickhouse-debug               0.928     0.901       -2.91%
clickhouse-release             0.372     0.391       +5.11%
firefox-debug                  0.768     0.773       +0.65%
firefox-debug-arm64            0.912     0.926       +1.54%
firefox-release                0.205     0.200       -2.44%
godot-debug                    0.444     0.412       -7.21%
godot-release                  0.066     0.073      +10.61%
libmergedlo.so-debug           0.424     0.427       +0.71%
libmergedlo.so-release         0.184     0.173       -5.98%
pytorch-debug                  0.779     0.735       -5.65%
pytorch-release                0.144     0.133       -7.64%
tensorflow-debug               3.080     2.784       -9.61%
tensorflow-release             0.664     0.602       -9.34%
2026-08-30 23:23:50 +09:00
Rui Ueyama f41bfcd5c7 Import current mold Rust port
C++ reference: mold fbaaa75f9f

Benchmark protocol: 32 threads; one C++ warmup followed by five
interleaved measured runs per linker; median reported. Every linker
invocation was preceded by sync and followed by sync; sleep 1.

Workload                    C++ (s)  Rust (s)  Rust vs C++
blender-debug                 0.821     0.770       -6.21%
blender-release               0.187     0.165      -11.76%
chrome-debug-arm64            1.714     1.815       +5.89%
chrome-debug-x86_64           1.602     1.643       +2.56%
chrome-release-arm64          0.713     0.751       +5.33%
chrome-release-x86_64         0.608     0.636       +4.61%
clang-debug                   1.303     1.139      -12.59%
clang-release                 0.097     0.097       +0.00%
clickhouse-debug              0.917     0.906       -1.20%
clickhouse-release            0.379     0.386       +1.85%
firefox-debug-arm64           0.918     0.935       +1.85%
firefox-debug                 0.787     0.802       +1.91%
firefox-release               0.200     0.206       +3.00%
godot-debug                   0.445     0.418       -6.07%
godot-release                 0.066     0.075      +13.64%
libmergedlo.so-debug          0.424     0.439       +3.54%
libmergedlo.so-release        0.184     0.171       -7.07%
pytorch-debug                 0.794     0.736       -7.30%
pytorch-release               0.142     0.136       -4.23%
tensorflow-debug              3.043     2.808       -7.72%
tensorflow-release            0.657     0.610       -7.15%

Validation: cargo build --release; ./run-tests.sh --all (pass 5360,
skip 636, fail 0); cargo clippy --release --all-targets.
2026-08-30 22:46:00 +09:00
Rui Ueyama 504249d994 Merge pull request #1641 from jvoisin/bti
Support -z rewrite-endbr on arm64
2026-08-29 15:27:13 +09:00
Rui Ueyama ecf335a4e1 Update README 2026-08-29 14:40:31 +09:00
Rui UeyamaandClaude Fable 5 fbaaa75f9f Verify SHA-256 hashes of source archives in dist.sh
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-27 17:08:32 +09:00
Rui UeyamaandClaude Fable 5 91cab12f9a Fix CI
mold demangles with the platform's __cxa_demangle, and FreeBSD's libcxxrt
spells `operator delete[]` as `operator delete []`, so the quoted name in
the test never matched there. Test literal matching of brackets with
`S::operator[](int)` instead, which every demangler spells the same way.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-27 12:27:34 +09:00
jvoisin 3d4dc7291e Support -z rewrite-endbr on arm64
So far -z rewrite-endbr was implemented only for x86-64, where it
rewrites unnecessary ENDBR64 landing pads (Intel CET) with NOPs for
functions whose addresses are not taken. arm64 has an equivalent
control-flow-integrity feature, BTI, whose landing pad is `bti c`.

This commit generalizes rewrite_endbr into a template shared by both
targets: the landing pad and NOP byte patterns are selected per-arch
(ENDBR64/NOP on x86-64, `bti c`/`nop` on arm64). The whole-program
analysis that preserves landing pads for address-taken functions is
unchanged.

Unlike x86-64, arm64 needs range extension thunks, and a thunk reaches
its target with an indirect branch (`br x16`). A function that is only
called directly can therefore still be entered indirectly once its call
is routed through a thunk, so we must keep its landing pad. We preserve
the landing pad of every thunked symbol; the thunk symbol set has
already been narrowed to the ones that actually need a thunk by
remove_redundant_thunks().

Note that on arm64 with recent glibc, `main`'s landing pad is
legitimately removed: the CRT passes a local __wrap_main stub (whose
landing pad is kept) to __libc_start_main and reaches main through a
direct branch, which does not require a landing pad.

Also note that code built with -mbranch-protection=standard (BTI +
pointer authentication) starts functions with `paciasp`, which doubles
as a landing pad, so there is no separate `bti c` to rewrite and this
option has no effect on such code.

This was locally tested on an Apple M2 machine running Asahi Linux.

Fixes #1552.
2026-08-26 17:52:23 +02:00
Rui UeyamaandClaude Fable 5 e633272dc9 Range-check R_PPC64_TOC16_DS on PPC64 ELFv2
The relocation has a 16-bit displacement from the TOC pointer, but the
ELFv2 backend applied it without a range check and silently truncated
displacements to .toc entries beyond 32 KiB, producing a load from the
wrong address. The ELFv1 backend already checked it; do the same here.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:18:56 +09:00
Rui UeyamaandClaude Fable 5 5dce8bb4fc Report .eh_frame relocations that do not fit in 32 bits
FDEs refer to their functions with 32-bit PC-relative relocations, and
.eh_frame_hdr stores 32-bit offsets, but mold applied them without any
range check. A function more than 2 GiB away from .eh_frame, which
happens with a huge text section or with -Ttext or --section-start
placing code far from the default layout, silently got a wrapped-around
FDE that the unwinder cannot use, while GNU ld and lld reject such
links. Those relocations are now range-checked like all others, and so
are the .eh_frame_hdr entries.

The aarch64 long thunk test placed code with unwind tables 4 GiB away
from .eh_frame and relied on the bogus FDEs going unnoticed. It now
compiles the far functions without unwind tables and keeps the far
section fixed while swapping the functions between the sections, so
calls in both directions are still covered.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:18:56 +09:00
Rui UeyamaandClaude Fable 5 28513b5882 Match quoted names in version scripts and dynamic lists literally
GNU ld takes a quoted name in a version script or a dynamic list as a
literal string, while an unquoted one is a glob pattern. mold dropped
the quotes and treated everything as a pattern, so `"operator
delete[](void*)"` in an `extern "C++"` block never matched anything
because `[]` parsed as an empty character class. Glob metacharacters in
a quoted name are now escaped before the name is handed to the matcher.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui UeyamaandClaude Fable 5 df35e2ba4a Accept local: and global: without a following space in version scripts
The tokenizer keeps colons within a token for the C++ scope operator,
so `local:*;` lexed as a single token that the parser then took for a
global symbol pattern named `local:*`, and nothing was localized. The
label is now split off such a token.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui UeyamaandClaude Fable 5 72a956d56e Do not interwork ARM32 branches to symbols that are not functions
mold took the instruction set of a branch target from the LSB of the
symbol value alone, but only STT_FUNC symbols encode the instruction
set that way. A Thumb BL to a plain label in hand-written assembly,
which is NOTYPE and has an even address, was rewritten to BLX and
crashed with SIGILL, and a Thumb B.W to it went through a mode-switching
thunk. Like GNU ld, we now switch the instruction set only for function
symbols and for PLT entries, which are always ARM code.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui UeyamaandClaude Fable 5 32e7a1a111 Synthesize the PPC64 _savefpr, _restfpr, _savevr and _restvr routines
With -Os, GCC calls linker-synthesized routines not only to save and
restore general-purpose registers but also floating-point and vector
registers. mold only provided the former, so such links failed with
undefined symbol errors. The new routines use the same layout and
encodings as GNU ld; the section that holds them is renamed to
.save_restore_regs accordingly.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui UeyamaandClaude Fable 5 64d7331cef Pass common symbols through to a relocatable output
With -r, common symbols are not converted to .bss, but to_output_esym()
had no case for them and wrote them out as absolute symbols whose value
was their alignment, so linking the -r output again resolved them to a
bogus fixed address. They are now emitted as SHN_COMMON symbols with the
alignment in st_value, as in the input files.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui UeyamaandClaude Fable 5 1e5d6ba386 Fix the alignment inferred from R_RISCV_ALIGN with a power-of-two addend
The addend of R_RISCV_ALIGN is the number of NOP bytes the assembler
emitted, which is the requested alignment minus the minimum instruction
size, so the alignment is the smallest power of two greater than the
addend rather than the addend rounded up to a power of two. `.balign 4`
in RVC code (addend 2) and `.balign 8` in non-RVC code (addend 4) were
therefore treated as 2- and 4-byte alignments; in a static link that
left glibc's __sigsetjmp at a 2 mod 4 address.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 19:17:50 +09:00
Rui Ueyama 04a455a416 Bump mimalloc version to 3.5.0
Fixes https://github.com/rui314/mold/issues/1639
2026-08-26 10:12:28 +09:00
Rui UeyamaandClaude Fable 5 d00482d2dd Allow far calls to unresolved weak functions
An unresolved weak function resolves to address 0. In a huge executable,
a call site can be more than 2 GiB away from it, so a call relocation
such as R_X86_64_PLT32 cannot encode the displacement and mold reported
an out-of-range error. Such a call is never executed because the program
has to null-check the function first, so we now skip the range check for
call relocations against such symbols and write the truncated value.
This covers x86-64, RISC-V, s390x, SPARC64 and LoongArch; ARM already
rewrites such calls to NOPs, and PowerPC calls go through thunks. Data
references keep their range checks because their results feed the null
check.

This fixes linking Godot's sanitizer build, whose .text ends 70 MB past
the 2 GiB mark.

Fixes https://github.com/rui314/mold/issues/1640

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 09:12:20 +09:00
Rui UeyamaandClaude Fable 5 7279781fbd Define __global_pointer$ inside the image if there's no .sdata
Without .sdata, we defined RISC-V's __global_pointer$ as absolute
address 0. Since crt1.o initializes gp with a PC-relative
`lla gp, __global_pointer$`, such an executable could not be linked
more than 2 GiB away from address 0, for example with -Ttext-segment.
We now define the symbol at the beginning of the image, like GNU ld
which defines it relative to .data or .bss in that case. The exact
value doesn't matter because we don't relax gp-relative accesses.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-26 09:09:51 +09:00
Rui Ueyama f716fb62de Fix test for some targets
Fixes https://github.com/rui314/mold/issues/1638
2026-08-23 21:24:11 +09:00
Rui UeyamaandClaude Fable 5 d9c9b906ad Count section piece symbols while parsing sections
reattach_section_pieces() traversed every relocation of every alloc
section once just to count the references to mergeable sections, so
that it could allocate their symbols at once, and then traversed them
again to create the symbols. The count needs only ELF data, so it is
now taken while the sections are parsed, where a CREL relocation table
has just been decoded and is still in cache.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:09:05 +09:00
Rui UeyamaandClaude Fable 5 364413c41b Scan response file tokens with a lookup table
The tokenizer tested each byte against six whitespace characters and
three quoting characters in turn. A table makes the scan a single
load and compare per byte, and halves the time to tokenize Chromium's
1.9 MiB response file.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:09:05 +09:00
Rui UeyamaandClaude Fable 5 8d9cdfef3d Cut per-symbol overhead in register_global_symbols()
The loop looked up every undefined symbol in the --wrap set even when
no --wrap option was given, hashing 5.1 million long C++ names on a
Chromium link, and fetched the thread's ShardedMap bin through TBB's
thread-local lookup once per symbol rather than once per file.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 21042fa988 Flatten output section members in parallel
create_output_sections() copied each output section's members from its
per-file vectors into the final array on one thread and then freed the
vectors one by one, which took about 10 ms on a Chromium link. Copy
and free per file in parallel instead, computing the alignment in the
same pass.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 00f3077dae Skip defined symbols without locking in claim_unresolved_symbols()
The pass took each symbol's lock once per undefined reference, 5.1
million times on a Chromium link, although nearly every reference is
to a defined symbol, which the pass leaves alone. Popular symbols are
referenced from thousands of files, so their locks bounced between
cores. Find the references to symbols without a definition first,
which needs no lock because the pass does not change defined symbols,
and take the locks only for those.

Chromium release on a Threadripper 7980X at 32 threads: the pass goes
from 21 ms to 2 ms. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 97f9e8e416 Resolve only the affected symbols when a hidden symbol hits a DSO
When a symbol with hidden visibility resolved to a DSO, symbol
resolution started over from scratch for all symbols with that DSO
definition excluded, and Chromium has three such symbols. Resolution is
independent per symbol, so only those symbols are resolved again, and
if one of them moves to an archive member that is not reachable yet,
liveness is propagated from that member alone. The final resolution
pass after section parsing recomputes everything else as before.

Chromium release on a Threadripper 7980X at 32 threads: resolve_symbols
goes from 143 ms to 115 ms. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 8579808892 Sort input files in parallel
Sorting the 44,683 files of a Chromium link into command line order
took 7 ms on one thread at the end of the input reading phase.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 68fceadb7d Collect absolute relocations without per-member vectors
scan_abs_relocations() gathered each member's absolute relocations into
a vector of its own and then flattened the vectors on one thread. For
.data.rel.ro of a Chromium link that was 147,000 vectors holding 1.1
million entries, and the flattening and freeing took about 10 ms of
serial time. Count the entries per member first and write them to
their final positions in parallel instead.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 29a43f104d Open thin archive members from their own jobs
The job that read a thin archive opened all of its members before
feeding them to the reader loop, so a large archive's members were
opened one after another on one thread. Chromium's libblink_core.a
has 2,424. The archive job now only lists the member paths, and each
member's job opens its file, in parallel with the others.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 38ec6e4ad3 Register mergeable sections without a lock
Each MergeableSection added itself to its MergedSection's member list
under a mutex from the parallel conversion loop. An output has only a
few merged sections, so on a Chromium link, 32 threads took the same
mutex 150,000 times and spent most of the pass in futex calls. Collect
the members from the files after the loop instead.

Chromium release on a Threadripper 7980X at 32 threads:
create_merged_sections goes from 55 ms to 34 ms and its system time
from 0.74 s to 0.07 s. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 4af8d3f8e9 Read small input files instead of mmap'ing them
mmap(2) takes the process's address space lock, so with tens of
thousands of input files, the calls serialize at a few microseconds
each no matter how many threads make them. Linking Chromium maps 44,683
files, and that serialization alone bounds the input reading phase at
about 220 ms, more than a third of the link. Files up to 32 KiB, which
are 64% of Chromium's inputs by count but 16% by size, are now read
into malloc'ed buffers instead; read(2) takes no such lock, and larger
files are still mmap'ed so that their pages are shared with the page
cache rather than copied.

Chromium release on a Threadripper 7980X at 32 threads: the input
reading phase goes from 228 ms to 108 ms and the whole link from 0.69 s
to 0.56 s, with peak RSS unchanged. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 e344452b1e Speed up response file tokenization
The tokenizer pushed every byte through an ostringstream and copied
each token twice. Tokens without quotes or backslashes, which is nearly
all of them, are now substrings of the mapped file, and whitespace is
tested inline instead of through isspace().

Expanding Chromium's 1.9 MiB response file of 19,000 arguments took
33 ms of single-threaded startup on a Threadripper 7980X; it now takes
5 ms. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 21:08:48 +09:00
Rui UeyamaandClaude Fable 5 e3ae2eb7ff Process absolute relocations in parallel
scan_abs_relocations() classified an output section's absolute
relocations and copy_buf() applied them in serial loops, although
write_dynrels() already processes them in parallel shards. A PIE
Chromium has 1.1 million of them in .data.rel.ro alone, so each loop
ran for about 30 ms on one thread while the others waited.

Chromium release on a Threadripper 7980X at 32 threads: scan_relocations
goes from 84 ms to 50 ms, copy from 89 ms to 75 ms, and the whole link
from 0.90 s to 0.86 s. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 17:06:02 +09:00
Rui UeyamaandClaude Fable 5 6f0f855ce6 Test the visited bit before setting it atomically in GC
Most relocations refer to a section that another thread has already
visited, yet visit() always did an atomic fetch_or, so popular sections
bounced between cores on every reference. Check the bit with a plain
load first, as Atomic::test_and_set() does; it is never cleared during
marking, so a stale positive answer is still correct.

Chromium release on a Threadripper 7980X at 32 threads: the mark phase
goes from 65 ms to 25 ms, the whole link from 0.96 s to 0.92 s. Output
files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-23 16:54:49 +09:00
Rui UeyamaandClaude Fable 5 c2822be61a Skip the DWARF 5 type unit test if GDB chokes on the input
GDB 10 aborts on GCC's DWARF 5 type units even when the executable
contains no .gdb_index section, which broke CI on Debian 11. Probe GDB
with an index-less executable first and skip the test if it cannot
print a type from it.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CKdZWtcaghcU7m5zoZtMfN
2026-08-21 20:46:39 +09:00
Rui Ueyama 0d3fe4ed34 Simplify DWARF unit header parsing 2026-08-21 16:35:44 +09:00
Rui Ueyama 6b22256da3 Fix DWARF 5 type unit gdb index compatibility
Emit version 9 indexes when type units are present so GDB 12 falls
back instead of interpreting DWARF 5 type-unit offsets as
.debug_types. Keep version 7 for indexes without type units.

Allow the regression test to run with older readelf versions and skip
it under TSAN, where relocatable links hit known oneTBB races.
2026-08-21 15:32:25 +09:00
Rui UeyamaandClaude Fable 5 0ddd5b0420 Support DWARF 5 type units in gdb index
With -gdwarf-5 -fdebug-types-section, GCC moves each type into its own
type unit and emits it as a one-section COMDAT .debug_info contribution,
so an object file contains several .debug_info sections. We assumed one
.debug_info section per file containing nothing but compilation units:
type units were missing from the index, and a pubnames offset could be
interpreted relative to the wrong contribution.

Read every live .debug_info contribution instead, classifying units by
their header's unit_type. Type units are collected into the .gdb_index
type-unit table along with their signatures, and each pubnames set is
matched to its contribution through the relocation on the set header's
debug_info_offset field. COMDAT group selection has already discarded
duplicate type units by the time we read them, so exactly one copy of
each type unit is indexed.

Fixes https://github.com/rui314/mold/issues/1634

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-21 13:42:04 +09:00
Rui Ueyama 02a4923ba2 Rename a variable 2026-08-21 10:56:25 +09:00
Rui UeyamaandClaude Fable 5 8349c41980 Fix spurious duplicate symbol error with mixed LTO and non-LTO archives
If an IR file claims a COMDAT group, the LTO result provides the group's
contents. However, post-LTO COMDAT group selection started from a clean
slate, so an archive member extracted only after LTO could win a claimed
group, resurrecting a copy that collides with the LTO result.

Remember the claim on the signature symbol, and let only LTO-generated
files win such a group after LTO. LLVM keeps claimed groups in its
output; GCC emits their contents without a group, so no file wins.

Fixes https://github.com/rui314/mold/issues/1637

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-21 10:56:25 +09:00
Rui Ueyama abd0a19cfb Rename a class 2026-08-20 23:43:53 +09:00
Rui Ueyama e3e5fba05f Fix a -Wmaybe-uninitialized warning 2026-08-20 22:57:04 +09:00
Rui Ueyama cb33534e38 Simplify 2026-08-20 22:49:01 +09:00
Rui UeyamaandClaude Fable 5 c4c090bce7 Store relaxation deltas in the arena
shrink_section() fills r_deltas once, and the later passes only read it,
so it does not need to be a growable vector afterwards. Build the deltas
in a local vector and copy them into the arena. This shrinks InputSection
by eight bytes on RISC-V and LoongArch and makes the class trivially
destructible on every target, so InputSection objects can live in the
arena without an owner that runs their destructors.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-20 22:49:01 +09:00
Rui Ueyama 6e1d9b68a3 Pack InputSection state to save eight bytes
Several InputSection booleans were stored separately even though one
atomic byte is sufficient. Pack liveness, GC visitation, address
significance, decompression, and ICF-removal state into flags, and add
relaxed fetch_or/fetch_and operations for updating them.

Also remove fde_end by marking the final FDE in each section range in
existing FdeRecord padding. This reduces InputSection<X86_64> from 56 to
48 bytes without increasing FdeRecord or Symbol.
2026-08-19 16:02:44 +09:00
Rui Ueyama 7187b766f7 Reuse InputSection storage across ICF phases
The output offset, dense ICF index, and arena-relative ICF leader are
needed at disjoint points in the link. Keeping separate members wastes
space and requires a separate eligibility flag.

Store them in a union, using icf_idx == -1 to represent ineligibility.
Process the gathered ICF sections directly and retain icf_leader only for
eliminated sections after ICF, allowing leaders to restore their offsets.
2026-08-19 16:02:44 +09:00
Rui Ueyama ebb41cbd65 Reduce ICF class-counting overhead
Counting distinct ICF classes costs about as much as a digest
propagation round, but it was done after every round. Since refinement
only splits classes, convergence does not need to be checked that often.

Run three propagation rounds between class counts and move digest-buffer
swapping into propagate(). This reduces counting overhead while preserving
the final partition.
2026-08-19 16:02:44 +09:00
Rui Ueyama 30765e5020 Remove redundant command line option 2026-08-18 20:25:01 +09:00
Rui Ueyama 6a1b0f7924 Merge pull request #1636 from WaterWhisperer/fix-1632
Fix .srodata output name and LTO option spelling
2026-08-18 20:20:11 +09:00
Rui Ueyama dd3cdb3c9d Report the COMDAT owner for ODR violations
Name the object containing the prevailing COMDAT group so users can
identify the conflicting definitions.

https://github.com/rui314/mold/issues/1635
2026-08-18 17:14:50 +09:00
WaterWhisperer 5147fc580c Fix .srodata output name and LTO option spelling 2026-08-18 16:09:19 +08:00
Rui Ueyama e04dded86e Fix a crash on ODR violations
When an ODR violation leaves a relocation referring to a discarded COMDAT
section, ObjectFile::scan_relocations() reports an error, but
scan_relocations() continues with the absolute-relocation pass. That pass
assumes symbols are resolved and may dereference a null Symbol::file,
causing mold to crash instead of exiting with the diagnostic.

Add a checkpoint between the two relocation passes so mold exits before
inconsistent symbol state reaches the absolute-relocation scan.

Fixes https://github.com/rui314/mold/issues/1635
2026-08-18 15:22:46 +09:00
Rui UeyamaandClaude Fable 5 a718dbbcd5 Fix crash when linking CREL object files with -r
create_comdat_group_sections assumed that a relocation section in a
comdat group is always SHT_RELA or SHT_REL. If an input file uses the
CREL format, the relocation sections are SHT_CREL instead, so we took
the branch for ordinary member sections and dereferenced a null
pointer, as mold does not create an InputSection for a relocation
section. mold -r had been crashing on such files since CREL input
support was added in mold 2.38.0.

Recognize SHT_CREL the same way as SHT_RELA/SHT_REL so that the
member's relocation section is propagated to the output group.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-15 09:27:02 +09:00
Rui UeyamaandClaude Fable 5 a7504210dd Store input and output sections in the arena
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-15 09:27:02 +09:00
Rui Ueyama d8f4506ab5 Store InputSection file as a pointer 2026-08-14 22:15:34 +09:00
Rui Ueyama 5ef9acad1f Cache arena allocations in per-thread blocks 2026-08-14 22:15:34 +09:00
Rui Ueyama 3d16156250 Store section fragment references as 32-bit offsets 2026-08-14 18:19:42 +09:00
Rui Ueyama c340272e63 Fix CI 2026-08-13 13:01:48 +09:00
Rui Ueyama 635956d3b7 Emit correct symbol indices for local symbols with --emit-relocs
Commit 35f4a334 changed Symbol::is_local() to check visibility and
version-script localization, but get_output_sym_idx() also relies on it
to decide whether a symbol's slot offset is relative to the local or the
global partition of .symtab. An input-local symbol has default
visibility and no version, so is_local() returned false for it, and a
relocation emitted by --emit-relocs against a named local symbol
referred to an arbitrary global symbol.

This went unnoticed on most targets because GAS usually redirects
relocations against local symbols to section symbols, which take a
different code path. On LoongArch and RISC-V, the assembler keeps them
as-is because linker relaxation may change section-relative offsets, so
relocations against symbols like .LC0 were visibly wrong.

Fixes https://github.com/rui314/mold/issues/1631
2026-08-13 12:33:34 +09:00
Rui Ueyama 5f71cbf988 Write tombstones to .eh_frame_hdr for zero-length FDEs
Compilers may emit a meaningless FDE covering a zero-length address
range, e.g. for a function fragment that hot/cold splitting has
entirely optimized away. Such an FDE begins at the same address as the
FDE of the next real function, giving the binary search table in
.eh_frame_hdr duplicate keys. If a search lands on the empty FDE
instead of the real one, the unwinder fails to find an existing
exception handler and aborts the process.

We now write a tombstone value to .eh_frame_hdr for such an FDE. The
tombstone compares greater than any real address, so a binary search
never returns it. The FDE itself is harmless because an empty address
range doesn't match any program counter.
2026-08-13 12:14:21 +09:00
Rui Ueyama 6157cc99f1 Pack AArch64 range extension thunks
AArch64 range extension thunks are small and unlikely to benefit
measurably from aligning every entry to a 16-byte boundary. Padding
every entry with brk instructions wastes meaningful space when an
output contains many thunks.

Keep each thunk group 8-byte aligned, but pack its entries tightly so
the ARM64 short and long forms occupy 12 and 24 bytes instead of 16
and 32 bytes.
2026-08-12 20:02:27 +09:00
Rui Ueyama 441dc1d1fd Update mold.1 (automated commit) 2026-08-11 04:42:53 +00:00
Rui Ueyama d12e0e4e9c Bump mold version to 2.42.0 2026-08-11 11:34:45 +09:00
Rui Ueyama f76ea9442a Speed up range extension thunk tests 2026-08-11 11:34:45 +09:00
Rui Ueyama b9d57111ed Fix a RISC-V crash issue 2026-08-11 10:39:12 +09:00
Rui Ueyama de35271267 Upgrade bundled mimalloc to 3.4.5 2026-08-11 10:39:12 +09:00
Rui Ueyama cf0e0f7996 Fix relocations against undefined weak TLS symbols
A weak TLS variable is usually guarded by a separate weak non-TLS
symbol indicating whether the TLS definition is available. A typical
use looks like this:

  extern char foo_used __attribute__((weak));
  extern __thread int foo __attribute__((weak));

  if (&foo_used)
    foo = 1;

If foo remains undefined, its access is unreachable and there is no
meaningful value to apply to its TLS relocations. Leave those
relocations unapplied.

This prevents AArch64 from rejecting an otherwise unreachable TLS
access when an R_AARCH64_TLSLE_ADD_TPREL_HI12 relocation is out of range.

https://github.com/rui314/mold/issues/1626
2026-08-11 10:39:12 +09:00
Rui Ueyama a94df206b6 Fix SH4 PLT delay slots
SH4 raises a slot-illegal exception when a PC-relative load is placed
in a branch delay slot. Our PLT entries did exactly that:

  jmp     @r0
  mov.l   2f, r1

QEMU now checks for such illegal delay-slot loads as of
https://github.com/qemu/qemu/commit/b754cb2dcde26a7bc8a9d17bb6900a0ac0dd38e2,
matching real hardware and the SH-4 ISA. Older QEMU versions accepted
this sequence because they did not enforce the restriction, so upgrading
QEMU caused dynamically linked mold output to trap when entering a PLT
slot.

Move the relocation-index load before the jump and put a nop in the
delay slot. Grow each PLT entry from 16 to 20 bytes to make room while
keeping the two literal values 4-byte aligned.
2026-08-11 10:39:12 +09:00
Rui Ueyama 1ce5b39f7e Install loongarch64 build dependencies from the main Debian archive 2026-08-11 10:14:46 +09:00
Rui Ueyama 80af3a8138 Use the official distribution site instead of unreliable mirrors 2026-08-10 21:32:11 +09:00
Rui Ueyama 799b26f8e2 Embed git hash in distribution binaries 2026-08-09 22:07:41 +09:00
Rui Ueyama a9dc01b477 Consolidate Gentoo test caches 2026-08-07 10:59:58 +09:00
Rui Ueyama ac73431954 Support -Ttext-segment 2026-08-07 10:47:29 +09:00
Rui UeyamaandClaude Fable 5 3eca43961d Fix a SIGPIPE race in two tests
`exit` in an awk script that follows readelf in a pipeline closes the
pipe as soon as the first match is found. If readelf has not finished
writing its output by then, it is killed by SIGPIPE, which pipefail
turns into a test failure. The race is timing-dependent and was
observed roughly once per thousand runs on a loaded machine.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-07 00:07:57 +09:00
Rui Ueyama 4b56395f5e Construct RELR once per output chunk 2026-08-06 22:28:26 +09:00
Rui Ueyama cf0b26045f Use ArenaPtr to refer to symbol aux records 2026-08-06 20:49:13 +09:00
Rui UeyamaandClaude Fable 5 283238426e Overhaul the Gentoo test harness
This rewrites lib/gentoo-test.sh to make a full-distro run practical
and its results easier to trust:

- Build each package with mold first, and run the GNU ld control build
  only when the mold build fails. Results are classified into success/,
  failure/ (builds with GNU ld but not with mold, i.e. a mold bug) and
  broken/ (doesn't build with either linker, so not our fault).

- Run test suites only for the package under test by scoping
  FEATURES=test through package.env. Applying it globally drags in the
  test dependencies of the entire dependency graph and makes a large
  part of the tree unresolvable.

- Let emerge apply the configuration changes it would otherwise just
  suggest (--autounmask-continue). An ephemeral container has no
  hand-maintained package.use, so without this, thousands of packages
  are unbuildable out of the box.

- Bring the container image to a consistent ~amd64 state with a world
  update at image build time. A stable stage3 base with testing
  packages on top otherwise accumulates unsolvable dependency
  conflicts, such as perl disagreeing with its installed virtuals.

- Share work between containers through host bind mounts: source
  tarballs are downloaded once into a shared DISTDIR, and binary
  package pools let each dependency be built once instead of once per
  container that needs it. The mold and GNU ld runs use separate pools
  so that a control build can never install mold-linked binaries.

- Serialize the container image build with flock. Parallel invocations
  used to race to build the same image, one per job slot.

- Work around a deadlock in portage's binary package extraction with
  lib/gentoo-gpkg-workaround.py: tar_stream_reader.close() waits for
  the external decompressor to exit without draining its stdout, which
  hangs when the undrained residue exceeds the pipe buffer.

- Refuse to run if dist/mold doesn't embed a git commit hash, since
  test results are keyed by it.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-06 19:41:05 +09:00
Rui Ueyama 8aa9429728 Fix CI 2026-08-02 16:30:01 +09:00
Rui Ueyama cdbc8db1cb Fix -Wdeprecated-enum-enum-conversion 2026-08-02 13:08:12 +09:00
Rui UeyamaandClaude Fable 5 a7b79b2a52 Prefetch hash table buckets when resolving mergeable sections
MergeableSection::resolve_contents inserts every section fragment into
the parent's ConcurrentMap. The table is typically much larger than
the cache, so each insertion stalls on a cache miss for its first
probe. Since split_contents has already computed all fragment hashes,
prefetch the bucket eight insertions ahead.

On firefox-debug at 32 threads, this cuts the CPU time of
compute_section_sizes, which performs the insertions, by about 15%
(2.7 to 2.3 CPU-seconds) and the pass's wall time from 0.11 s to
0.10 s; the end-to-end change is within noise. All 5809 tests pass.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-02 12:27:25 +09:00
Rui UeyamaandClaude Fable 5 ad038099a0 Increase ConcurrentMap shard count from 16 to 64
get_sorted_entries splits the bucket array into NUM_SHARDS pieces, and
MergedSection::compute_section_size assigns fragment offsets with one
task per shard, so the shard count caps that pass's parallelism at 16
threads even when more are available. Raise it to 64.

Probing is confined to a shard, and a probe that visits MAX_RETRY
occupied slots aborts, so MIN_NBUCKETS grows with the shard count to
keep at least MAX_RETRY buckets per shard in the smallest maps.

Since fragment offsets are assigned shard by shard, the layout of
merged sections changes slightly (inter-shard alignment padding).
Outputs remain bit-reproducible for a given linker version, which is
the guarantee mold makes.

On firefox-debug at 32 threads, compute_section_sizes goes from
0.13 s to 0.11 s; the end-to-end change is within noise. All 5809
tests pass.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-02 12:22:59 +09:00
Rui UeyamaandClaude Fable 5 c30224584a Recompute RELR layout data in parallel
RelDynSection::update_shdr runs several times per link because the
size of .relr.dyn feeds back into section layout: set_osec_offsets
calls it until the size converges, and compute_section_headers and
the final pre-copy refresh add more calls. Each call walked all
chunks serially and ended with a serial sort of every RELR offset.
On a Firefox debug link (~360k GOT slots, ~1M relative relocations),
this accounted for most of a ~0.2 s single-threaded stretch between
layout and output copy that is plainly visible in a CPU utilization
trace.

Three changes:

- GotSection::get_num_dynrels no longer materializes the full GOT
  entry array just to count relocations. Computing each entry's
  value involves a symbol address lookup, which dominated the cost.
  The counting logic mirrors get_got_entries' relocation choices.
  This is most of the win: the GOT was the longest pole on every
  call even with the collection parallelized.

- Collect per-chunk dynamic relocation counts and RELR offsets in
  parallel; chunks are independent of one another.

- Process chunks in address order when concatenating their RELR
  offsets. Each chunk emits its offsets in ascending order and
  chunks occupy disjoint address ranges, so the concatenation is
  sorted by construction and the global sort is gone.

The output is byte-identical on firefox-debug against the unpatched
linker at the same commit. All 5809 tests pass. An interleaved A/B
on firefox-debug shows the 32-thread link going from 1.18 s to
about 1.05 s (medians of five).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-02 11:22:06 +09:00
Rui Ueyama 5b84f386ee Simplify the hidden base-version symbol fix 2026-08-01 22:03:01 +09:00
Rui Ueyama 9b101aa9c7 Merge pull request #1629 from fmaroszek/fix-hidden-base-version-symbol
Skip hidden base-version symbols in DSOs
2026-08-01 21:40:22 +09:00
Rui Ueyama 3ff882b02c Collect RELR offsets directly from output chunks
RELR needs only the addresses to which relative relocations apply. Even after
the parent commit, mold creates complete REL/RELA records, partitions them in
RelDynSection, and then discards every field except r_offset.

Have each output chunk return a compact vector of its RELR offsets. Aggregate
and sort those vectors in RelDynSection, then encode them directly. Each chunk
also records how many of its dynamic relocations moved to RELR, so it can write
the remaining records straight to its final output slice.

Android+RELR keeps a temporary array for the remaining records because the
size of their APS2 encoding affects layout.

Seven interleaved 32-thread runs with --no-fork gave these median wall times:

                      Parent       This commit
  Firefox release    0.472518 s    0.396796 s    (-16.03%)
  Chromium debug     2.081396 s    2.044275 s    ( -1.78%)

Compared with 64b92151, the accumulated improvements are 17.79% for Firefox
release and 1.68% for Chromium debug.

An earlier seven-run A/B test found no consistent cost from returning each
vector instead of appending through an output parameter:

  Firefox release:  append 0.396 s, return 0.390 s
  Chromium debug:   append 2.005 s, return 2.028 s

The differences are less than 2% and point in opposite directions.

All 471 native x86-64 tests pass, with nine expected skips. A release build for
all supported targets also passes.
2026-08-01 17:17:03 +09:00
Rui Ueyama 19ae69dd61 Write dynamic relocations into exact output slices
RelDynSection currently asks each producer for a vector and appends it to a
second vector. OutputSection also builds one vector per shard and flattens
them. Large links therefore allocate, copy, and destroy relocation records
multiple times.

Have each producer report its exact relocation count on Chunk. RelDynSection
allocates one destination array and gives each producer a pointer to its exact
slice. OutputSection records per-shard prefix counts so shards can fill
disjoint slices in parallel.

Recompute counts on every layout pass because synthetic GOT relocation counts
can change before layout converges.

Seven interleaved 32-thread runs against 64b92151, with --no-fork, gave these
median wall times:

  Firefox release:  0.482679 s -> 0.472518 s  (-2.11%)
  Chromium debug:   2.079169 s -> 2.081396 s  (+0.11%)

All 471 native x86-64 tests pass, with nine expected skips. Release builds for
x86-64 and PPC64V1 also pass.
2026-08-01 17:17:03 +09:00
Rui UeyamaandClaude Fable 5 64b92151a0 Skip an unnecessary layout pass for non-Android links
After fix_synthetic_symbols, we re-ran set_osec_offsets
unconditionally. That pass is needed only under
--pack-dyn-relocs=android, whose packed relocation format encodes
addends in variable-length SLEB128, so fixing synthetic-symbol
values can change the encoded size of .rela.dyn. No other mode
emits anything whose size depends on addends; in particular,
RELR's size depends only on relocation locations, which are fixed
by this point. Make the pass conditional on the Android mode.

On the Firefox release benchmark with 32 threads and --no-fork,
this shortens the link from 0.44 s to 0.41 s. Outputs are
byte-for-byte identical before and after for both a RELR workload
(Firefox) and a RELA workload (Chromium release).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-31 21:45:43 +09:00
Rui Ueyama 5e33441a0d Iterate over prevailing symbols directly 2026-07-31 12:19:39 +09:00
Rui Ueyama dd06a8969b Match large glob sets with an NFA
The scalar glob matcher is faster for ordinary version scripts, but its
work grows linearly with the number of patterns. Compile sets of 64 or more
complex patterns into one bit-parallel NFA. Each state occupies one bit, so
a machine word advances 64 states at once. Keep the existing scalar and
Aho-Corasick paths unchanged for smaller and simpler pattern sets.

On 200,000 synthetic 60-byte symbol names, the standalone matcher changes
as follows:

  128 patterns: 1.00 s -> 0.197 s (5.1x faster)
  256 patterns: 1.98 s -> 0.380 s (5.2x faster)

A normal TensorFlow debug link remains on the scalar path. A sanity check
shows apply_version_script changing from 0.177 s to 0.175 s.
2026-07-31 11:59:53 +09:00
Rui Ueyama 0a59e34909 Speed up glob matching
The general glob matcher represents all patterns as one NFA and updates
several bit vectors for every byte of every symbol. Its cost therefore
depends on the total pattern length even when a version script contains only
a few complex patterns.

Compile each complex pattern into literal, star, question, and bracket
tokens. Match without recursion, remember the most recent star, and use
string_view::find for literal runs following a star. This replaces MultiGlob
and removes the Bitvector class.

Also store non-root Aho-Corasick edges as sibling lists. Most trie nodes have
one child, so this reduces a node from 1,040 to 24 bytes on a 64-bit host. A
dense root table keeps the common transition fast. Construct failure links
breadth-first, as required by the algorithm.

With symbol-table traversal held constant, the median of five 32-thread
TensorFlow debug links changes as follows:

  apply_version_script wall time: 0.503 s -> 0.179 s (-64%)
  apply_version_script CPU time:  14.137 s -> 5.467 s (-61%)
  total wall time:                 3.626 s -> 3.285 s (-9%)
2026-07-31 10:49:32 +09:00
Rui Ueyama 34a2edb995 Iterate over symbols when applying version scripts
Version script wildcard matching currently visits each object file and scans
its global symbol slots. A symbol is processed only when that file provided
the prevailing definition, so many entries are skipped. More importantly, an
entire file is one task, which leaves workers idle when large object files
reach the end of the pass.

Iterate over the symbol table instead. Its 64 shards visit every symbol once
and distribute the work more evenly. Rename ShardedMap::for_each to
parallel_for_each to make the scheduling behavior explicit.

With the same glob matcher and 32 threads, the median of five TensorFlow
debug links changes as follows:

  apply_version_script wall time: 0.699 s -> 0.503 s (-28%)
  apply_version_script CPU time:  13.456 s -> 14.137 s (+5%)
  total wall time:                 3.800 s -> 3.626 s (-5%)
2026-07-31 10:49:32 +09:00
Franciszek MaroszekandClaude Opus 5 5898fbcb6e Skip hidden base-version symbols in DSOs
A defined dynamic symbol at VER_NDX_GLOBAL with the VERSYM_HIDDEN bit set
is a legacy compat alias. GNU as emits one for `.symver foo, foo@VER`
without the `remove` modifier, so that binaries linked before the symbol
was versioned keep resolving. It must not satisfy a new unversioned
reference to `foo`; that has to bind to the default version `foo@@VER`.

SharedFile::parse cleared VERSYM_HIDDEN before classifying the symbol, so
such an alias took the "unversioned symbol" path and claimed the plain
name. With both it and the `foo@@VER` definition claiming that name,
resolve_symbols keeps whichever comes first in .dynsym, which is the
alias. No verneed entry is then emitted, and glibc refuses to hand out a
non-base version for an unversioned lookup, so the alias wins at runtime
too and the caller silently gets the old implementation.

lksctp-tools' libsctp.so.1 exports sctp_connectx this way. Linking with
mold selects the pre-SCTP_SOCKOPT_CONNECTX3 ABI, which reports success
but never fills in the caller's association ID.

Skip such symbols while parsing. They cannot be referred to by any name,
as there is no version string to form `foo@VER` from. ld.bfd reports an
undefined reference when one is the only definition, which is what
skipping produces.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-30 14:23:20 +02:00
Rui Ueyama 7324c2a006 Fix 32 bit host build
Fixes https://github.com/rui314/mold/issues/1628
2026-07-30 10:16:50 +09:00
Rui Ueyama 48cea99498 Fix clang compiler warning 2026-07-28 21:11:29 +09:00
Rui Ueyama 755a39ed53 Fix CI 2026-07-28 21:02:49 +09:00
Rui Ueyama bf19884539 Fix CI 2026-07-28 18:48:14 +09:00
Rui Ueyama 17abd5cdea Skip repeated thunk range checks 2026-07-28 18:03:28 +09:00
Rui Ueyama dfb03f26f8 Shrink thunks concurrently
Each provisional thunk owns independent symbol and offset vectors. Filter
unneeded entries and recompute their offsets concurrently instead of
shrinking the thunks one at a time.

On the Chromium Arm64 debug link, seven interleaved A/B runs reduce the
remove_redundant_thunks median from 0.080 to 0.073 seconds and median
wall time from 1.924 to 1.897 seconds. The output is byte-identical.
2026-07-28 16:00:37 +09:00
Rui Ueyama 474d4ce4df Sort thunk symbols concurrently
Symbol order does not affect thunk placement, so defer deterministic sorting
until all thunks for an output section have been constructed. Sort independent
thunk vectors concurrently instead of sorting each one in the serial batch
loop.

On the Chromium Arm64 debug link, nine interleaved A/B runs reduce median wall
time from 1.98 to 1.97 seconds. The compute_section_sizes median falls from
0.116 to 0.095 seconds. The output is byte-identical.
2026-07-28 16:00:31 +09:00
Rui Ueyama 4cce716e72 Reduce serial work when creating thunks
Initialize input-section offsets in parallel. Section offsets and end offsets
are monotonic, so use lower_bound to find A and C instead of walking over
millions of sections a second and third time.

On the Chromium Arm64 debug link, five interleaved A/B runs reduce median wall
time from 2.01 to 1.95 seconds. create_range_extension_thunks falls from about
0.17 to 0.11 seconds. The output is byte-identical.
2026-07-28 16:00:31 +09:00
Rui Ueyama e4303913ee Avoid contention when creating thunks
Collect newly discovered thunk targets in per-worker vectors and concatenate
them after each batch. The existing atomic flag still removes duplicates, and
the final sort preserves deterministic output without serializing every vector
insertion through one mutex.

On the Chromium Arm64 debug link, five interleaved A/B runs reduce median wall
time from 2.19 to 2.03 seconds. The compute_section_sizes phase falls from
0.341 to 0.181 seconds, and its system CPU time falls from 5.146 to 0.620
seconds. The output is byte-identical.
2026-07-28 16:00:31 +09:00
Rui Ueyama efbba44cdc Compact mergeable section fragments
Allocate MergedSection objects in the linker arena and store each fragment's
parent section as a 32-bit arena index. This shrinks SectionFragment from 24
to 16 bytes and its ConcurrentMap bucket from 64 to 32 bytes.

Chromium's x86-64 debug link contains about 2.17 million merge-map buckets.
Across five interleaved 32-thread runs, median wall time changes from 1.91 to
1.90 seconds and median peak RSS falls from 24,870,408 to 24,808,228 KiB, a
reduction of 62,180 KiB.
2026-07-28 16:00:31 +09:00
Rui Ueyama 6a9ecbad23 Build GDB index tables during layout
Build the symbol table and constant pool in a low-priority task after
debug-info offsets are fixed. The final output path then reads relocated
address ranges and copies the prepared tables.

The stages have different dependencies and useful parallelism. Reading
input name data needs only input sections and can use all available
workers. Building the table needs final .debug_info offsets but stops
scaling near 12 workers, so run it in a separate bounded arena. Reuse one
task group for both stages.

Keep the standard serial open-addressed hash construction. This preserves
deterministic output without the prototype atomic claim table and repeated
parallel probe rounds.

Against an unrestricted table arena on the same tree, the bounded arena
reduced the 10-run median from 1.935 s to 1.905 s and user CPU from
43.510 s to 42.865 s.

Chromium debug link, 32 threads, 10 interleaved run medians:

  wall:       1.975 s -> 1.900 s (-3.8%)
  user:      43.125 s -> 42.600 s (-1.2%)
  system:     9.740 s ->  9.770 s (+0.3%)
  peak RSS: 24,817,662 -> 24,889,408 KiB (+70 MiB)
2026-07-28 16:00:31 +09:00
Rui Ueyama 046101eda3 Store input section contents as a pointer
Use the ELF section header for the stored input size and keep only the
data pointer in InputSection. Decompressed sections use sh_size because
their replacement buffer has the uncompressed size.

This reduces InputSection<X86_64> from 88 to 80 bytes. Relaxed RISC-V
and LoongArch sections continue to use the original section-header size
when copying their input bytes.

Chromium debug link, 32 threads, 10 interleaved run medians:

  wall:       1.960 s -> 1.950 s (-0.5%)
  user:      42.565 s -> 42.560 s
  system:     9.775 s ->  9.615 s (-1.6%)
  peak RSS: 24,834,922 -> 24,816,480 KiB (-18 MiB)
2026-07-28 16:00:31 +09:00
Rui Ueyama 594b03674d Combine COMDAT parsing and selection
Parse each reachable object and record its COMDAT candidates in one
parallel traversal. Reuse the signature name while parsing instead of
recovering it from the selected Symbol for the GCC debug-macro check.

Chromium debug link, 32 threads, 10 interleaved run medians:

  wall:    1.965 s -> 1.960 s (-0.3%)
  user:   43.160 s -> 42.615 s (-1.3%)
  system:  9.820 s ->  9.845 s (+0.3%)
2026-07-28 16:00:30 +09:00
Rui Ueyama 5f7764124c Use 32-bit references for linker metadata
Reserve a sparse address-space arena for symbols and input files. Objects in
that arena can refer to each other with four-byte self-relative pointers,
which halves the size of symbol-reference arrays and reduces Symbol from 48
to 40 bytes. On 32-bit hosts, reserve 256 MiB instead of 8 GiB; this places a
practical limit on linker metadata without affecting 32-bit targets linked on
a 64-bit host.

Keep the standard per-shard hash tables, but allocate their Symbol values in
stable arena blocks. Store global names beside those values, derive local
names from their owner files, and represent each COMDAT group in eight bytes.
Context still destroys file objects normally so their separately allocated
data is released before the arena is unmapped.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs falls from 1.990 to 1.970 seconds. Median user time changes from
42.880 to 43.015 seconds, while system time falls from 9.965 to 9.890 seconds.
Median peak RSS falls from 25,625,100 to 24,841,684 KiB, a reduction of
783,416 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama fc7cbd5dad Compact ELF section tables
Chromium has 41.96 million ELF section-table slots but only 5.86 million
regular input sections. Store a 32-bit value per slot instead of an
InputSection pointer. Zero denotes an absent section, the high bit denotes a
mergeable section, and the remaining bits index stable per-file pools.

InputSectionTable owns both pools and presents the same indexed and range
interfaces used by linker passes. Moving a section to the mergeable pool is
now explicit instead of relying on a constructor that clears a pointer passed
by reference.

Chromium debug link, 32 threads, eight interleaved run medians:

  wall:      2.005 s -> 2.000 s (-0.2%)
  user:     43.595 s -> 43.375 s (-0.5%)
  system:   10.160 s -> 9.865 s (-2.9%)
  peak RSS: 26,145,360 -> 25,598,862 KiB (-534 MiB)

All section contents matched apart from .comment and the fast build-ID note.
2026-07-28 16:00:30 +09:00
Rui Ueyama 1861e3c308 Radix-sort large GDB type lists
GDB index type vectors contain fixed-width 32-bit records. Sort lists with at
least 256 records using four byte-wise radix passes, while retaining comparison
sorting for smaller lists. Reuse worker-local scratch storage so the linear
path does not allocate for every name.

Chromium debug link, 32 threads, eight interleaved run medians:

  wall:     1.965 s -> 1.955 s (-0.5%)
  user:    44.720 s -> 43.890 s (-1.9%)
  system:   9.940 s -> 10.185 s (+2.5%)

The complete 4.86 GiB output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 1f77c59113 Share discarded local symbols
Chromium debug has 18.39 million local symbols, and 9.71 million belong to
discarded COMDAT sections. After COMDAT elimination, allocate Symbol objects
only for retained local symbols and map every discarded local symbol to one
global zero-valued placeholder. The ELF gABI requires local symbols in a
discarded COMDAT member to be discarded and forbids references to them from
outside the group, so the placeholder does not preserve per-symbol metadata.

Chromium debug link, 32 threads, six interleaved run medians:

  wall:     2.095 s -> 2.060 s (-1.7%)
  user:    48.095 s -> 46.735 s (-2.8%)
  system:  10.110 s -> 10.280 s (+1.7%)
  peak RSS: 26,946,944 -> 26,241,790 KiB (-0.67 GiB)

Chromium sections matched apart from version and fast build-ID metadata.
2026-07-28 16:00:30 +09:00
Rui Ueyama fb80d619ae Stream debug CREL relocations
Chromium debug contains 93.8 million relocations in nonallocated sections.
Its 230 MiB CREL input expands to about 2.1 GiB of ElfRel records. Decode
and apply those relocations in one pass instead of retaining the expanded
arrays. Keep exact arrays for allocated sections, whose relocations are
consumed later.

for_each_reloc calls a target-specific callback for every relocation. GCC
15 does not inline these large callbacks on its own, leaving a function call
for each ordinary REL or RELA relocation. Mark the callbacks ALWAYS_INLINE;
this has no effect on CREL's representation but avoids that regression.

Chromium debug link, 32 threads, six-run medians:

  wall:     2.215 s -> 2.115 s (-4.5%)
  user:    50.145 s -> 48.415 s (-3.5%)
  system:  11.065 s -> 10.215 s (-7.7%)
  peak RSS: 27.96 GiB -> 25.64 GiB (-2.32 GiB)

On an Asahi machine, adding ALWAYS_INLINE to the streaming implementation
changes the native clang-debug-arm64 link at 16 threads as follows:

  wall:  2.36 s -> 1.85 s (-21.6%)
  user:  35.3 s -> 27.1 s (-23.2%)

Chromium output matched apart from the fast build-ID payload. The two Asahi
outputs were byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama f554cfef24 Read GDB index inputs in the background
Compilation-unit headers and GNU pubnames do not depend on relocated
debug contents. Read them from per-file input sections and build the name
map in a low-priority TBB arena while foreground passes continue. Address
ranges and final serialization still wait until debug relocations are applied.

Chromium debug link, 32 threads, six-run medians:

  wall:     2.255 s -> 2.175 s (-3.5%)
  user:    49.670 s -> 49.485 s (-0.4%)
  system:  11.460 s -> 11.260 s (-1.7%)
  peak RSS: 27.95 GiB -> 27.98 GiB (+36 MiB)

The generated .gdb_index was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama d9137264f6 Reuse symbols for COMDAT groups
Most regular COMDAT signatures already name a global symbol. Reuse that
Symbol as the group identity instead of interning 14.8 million signatures in
a second map. Local, section, and versioned signatures use the same symbol
table through a name-based fallback.

Symbol resolution has been cleared when COMDAT owners are selected, so
sym_idx is temporarily available. Record the lowest file priority there, mark
each group whose file has that priority as the owner, and restore sym_idx with
one symbol-map traversal before normal resolution resumes. This keeps
ownership on the signature itself and needs no dense group IDs or auxiliary
side arrays.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs falls from 2.310 to 2.250 seconds. Median user time falls from
49.520 to 47.390 seconds, while system time changes from 11.720 to 11.935
seconds. Median peak RSS falls from 29,485,754 to 29,103,198 KiB, a reduction
of 382,556 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 7887246f23 Cache input symbol name lengths
ELF symbols already contain their string-table offsets. Cache only a one-byte
NameLen instead of retaining a 16-byte string_view for each input symbol.
Longer lengths use logarithmic lower bounds, so lookups scan only the uncached
suffix. Chromium has 37.8 million input symbols, making the per-symbol
reduction substantial.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs changes from 2.320 to 2.315 seconds. Median user and system time
fall from 49.800 and 11.625 seconds to 49.645 and 11.580 seconds. Median peak
RSS falls from 30,132,552 to 29,503,344 KiB, a reduction of 629,208 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama a222bda697 Store symbol name lengths compactly
Store short name lengths exactly in one byte and use the remaining values as
logarithmic lower bounds. Looking up a long name then scans only its suffix.
Copy a long version-prefix name into the string pool when its original storage
is not NUL-terminated. This reduces Symbol<X86_64> from 56 to 48 bytes.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs falls from 2.355 to 2.340 seconds. Median user time falls from
50.475 to 49.875 seconds, while system time is unchanged at about 11.85
seconds. Median peak RSS falls from 30,330,582 to 30,146,730 KiB, a reduction
of 183,852 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 63d88b5e46 Store input section names compactly
An input section's object and section header already identify its name in
the section-header string table. Store only the 16-bit length in InputSection
and recover the pointer from that existing metadata. This reduces
InputSection<X86_64> from 104 to 88 bytes.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs changes from 2.365 to 2.360 seconds. Median user and system time
fall from 50.940 and 12.005 seconds to 50.495 and 11.700 seconds. Median peak
RSS falls from 30,470,228 to 30,335,754 KiB, a reduction of 134,474 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama eb615efc16 Store decoded CREL in exact arrays
A CREL header gives the relocation count before decoding starts. Add
ExactArray, which owns an uninitialized array of a fixed size and provides
indexed access, and fill it directly. Existing relocation consumers receive a
span over the array.

This avoids vector capacity slack and initialization that the decoder
immediately overwrites.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time over
eight runs falls from 2.400 to 2.370 seconds. Median user and system time fall
from 51.655 and 12.115 seconds to 50.855 and 11.965 seconds. Median peak RSS
falls from 31,006,116 to 30,470,208 KiB, a reduction of 535,908 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 3af5464546 Speed up GDB index construction
Chromium contributes 35.8 million GDB name records. Keep each temporary
record at 16 bytes, replace its parsed form with its indexed form after
interning, and deduplicate names within each compilation unit with a small
linear hash table instead of sorting them.

Compute each string hash and length in one scan, assign constant-pool ranges
with a parallel prefix sum, and record type-vector slots while interning. Keep
the slot-claiming protocol shared by both ConcurrentMap insertion interfaces.

On the Chromium x86-64 debug benchmark with 32 threads, median wall time
over eight runs falls from 2.550 to 2.390 seconds. Median user and system time
fall from 54.735 and 12.390 seconds to 51.275 and 12.095 seconds. Median peak
RSS falls from 32,086,034 to 30,999,878 KiB, a reduction of 1,086,156 KiB.

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 50f68848d5 Use worker-local HyperLogLog sketches
The cardinality estimators used shared atomic registers even though their
callers already partition work across threads. Give each worker a local sketch
and merge the registers after the parallel scan. Apply the same scheme to GDB
index names and mergeable-section fragments.

Chromium debug link, 32 threads, five interleaved run medians:

  wall: 2.60 s -> 2.57 s (-1.2%)
  user: 56.22 s -> 55.95 s (-0.5%)

The generated .gdb_index was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama e1f1ba00e0 Avoid repeated section name scans
Section construction repeatedly recovered names from .shstrtab even when the
caller had just looked up the same name. Pass known names into constructors and
defer other lookups until a name is actually needed.

Chromium debug link, 32 threads, five interleaved run medians:

  wall: 2.61 s -> 2.60 s (-0.4%)
  user: 57.38 s -> 56.22 s (-2.0%)

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama 7813baa40f Avoid building discarded COMDAT sections
Duplicate COMDAT groups account for millions of sections in large debug
links. Read the symbol table and group membership first, choose the winning
groups after archive extraction, and only then construct input sections and
local symbols for retained members.

Let ShardedMap report whether gather created each symbol so new symbols can
be initialized in the same pass. An empty section table means that a file has
not reached section construction, avoiding a separate parsing-state flag.

Chromium debug link, 32 threads, five interleaved run medians:

  wall:     2.77 s -> 2.61 s (-5.8%)
  peak RSS: 33.2 GiB -> 30.9 GiB (-2.3 GiB)

The generated output was byte-identical.
2026-07-28 16:00:30 +09:00
Rui Ueyama ac7334246c Simplify 2026-07-28 16:00:30 +09:00
Rui Ueyama 9cb33a0e7d Merge canonical symbol flags
Canonical PLTs and copy relocations are alternative ways to give an
imported symbol an address in the output. Which one is needed follows
from the symbol type, so recording separate scan flags duplicates
information.

Use one CANONICAL action and NEEDS_CANONICAL flag, then choose the table
to populate when dynamic symbol entries are assigned.
2026-07-28 16:00:22 +09:00
Rui UeyamaandClaude Fable 5 5157cef1fc [PPC64] Reject files with an unsupported combination of ABI and endianness
We choose between the two PPC64 ABIs by endianness because ELFv1 is
normally big-endian and ELFv2 little-endian. However, that
correspondence is a convention rather than a rule. In particular, musl
uses ELFv2 on big-endian too, so linking for example Alpine ppc64
objects silently produced an ELFv1-style executable with a synthesized
.opd whose startup code crashes immediately.

We support only the usual combinations, so make get_elf_type() treat the
others as unrecognizable, just like an ELF file for an unsupported
machine. Also give unrecognizable ELF files a proper error message; we
used to print "incompatible file type: ppc64 is expected but got " with
a trailing empty string.

Fixes https://github.com/rui314/mold/issues/1498

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 14:43:12 +09:00
Rui UeyamaandClaude Fable 5 35f4a334a0 Do not demote non-exported globals to local symbols in .symtab
We computed the binding of a symbol in the static symbol table from its
dynamic import/export status, so in an ordinary executable, every global
symbol was emitted as STB_LOCAL. That misleads all symbol table
consumers; nm prints "t main" instead of "T main", and gdb had to work
around it for a feature that depends on symbol bindings.

Only symbols hidden by visibility or localized by a version script
should be demoted. GNU ld keeps hidden symbols STB_GLOBAL with hidden
visibility while lld converts them to STB_LOCAL; we follow lld, which
also matches our previous output for such symbols.

One wrinkle: claim_unresolved_symbols() assigned version index 0
(VER_NDX_LOCAL) to imported symbols so that their .gnu.version entries
would be 0, which would make the new is_local() misclassify them as
version-script-localized. Leave ver_idx unassigned for imported symbols
and have the .gnu.version writer emit 0 for unversioned undefined
symbols directly, so that VER_NDX_LOCAL on a symbol means only
"localized by a version script".

Fixes https://github.com/rui314/mold/issues/906

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 14:43:12 +09:00
Rui UeyamaandClaude Fable 5 fba0a2185d Report duplicate definitions of a versioned symbol
A default-versioned symbol `foo@@VER` can be referred to not only as
`foo` but also as `foo@VER`, so defining both `foo@@VER` and `foo@VER`
is a duplicate definition of the versioned name. We didn't diagnose it
and silently emitted a dynamic symbol table containing two definitions
of `foo@VER`, leaving it up to the dynamic loader which one a versioned
reference binds to. GNU ld and lld reject this; now we do too.

We check for duplicates by scanning the dynamic symbol table once it's
populated, because the problem manifests if and only if both symbols
are exported, and the exported set is much smaller than the set of all
symbols. GNU ld additionally reports the error for non-exported
symbols; lld doesn't, and we behave like lld.

Fixes https://github.com/rui314/mold/issues/828

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 14:43:12 +09:00
Rui UeyamaandClaude Fable 5 1fb2901b19 Report an error for a defined dynamic symbol with version index 0
We ignored defined dynamic symbols whose .gnu.version entry is 0
(VER_NDX_LOCAL) on the assumption that such symbols are bound locally.
That assumption is wrong; a symbol that's actually local to a DSO
doesn't appear in .dynsym at all, so such an entry is ill-formed. GNU
ld briefly emitted version index 0 for unversioned defined symbols
(https://sourceware.org/bugzilla/show_bug.cgi?id=33577), and linking
against a DSO created by such ld made us report confusing "undefined
symbol" errors for symbols that plainly exist in the DSO.

lld rejects such files as corrupted. Follow suit and report an error
pointing at the offending file and symbol instead of silently ignoring
it.

Fixes https://github.com/rui314/mold/issues/1534

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 13:46:57 +09:00
Rui UeyamaandClaude Fable 5 2b605279fe Report an error for a shared object in a static link
We silently accepted a shared object file given on the command line even
in the static mode and simply ignored it, producing an executable whose
references to the shared object's symbols remain unresolved. Such a
binary crashes at runtime. GNU ld and lld reject this at link time, and
so should we.

Fixes https://github.com/rui314/mold/issues/1616

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 13:46:57 +09:00
Rui UeyamaandClaude Fable 5 00a27307f8 Use git rev-parse to obtain the current commit hash
We used to read files under .git directly to avoid a build-time dependency
on git, but the on-disk repository format is not stable. The hash lookup
broke for repositories using the new reftable format, and it never worked
in git worktrees, where .git is a file rather than a directory.

Just ask git instead. Building from a git checkout now requires git, which
is not much to ask; building from a source tarball still works and simply
omits the hash, as before.

Fixes https://github.com/rui314/mold/issues/1621

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 11:36:14 +09:00
Rui UeyamaandClaude Fable 5 920a516125 Fix false ODR violation with mixed LTO and non-LTO archives
After LTO, we reset the reachability of archive members and redo symbol
resolution to re-derive which members are actually needed, because the
LTO-generated code may no longer use some of the members that were
extracted to satisfy references from IR files. However, the results of
the pre-LTO COMDAT group selection survived into the second resolution:
ComdatGroup::owner kept the first winner's priority, and the losers'
member sections remained dead.

Therefore, if a group's winner was an archive member that is not
re-extracted after LTO, no remaining file could provide the group's
sections, and we reported a false "refers to a discarded COMDAT section"
error for a perfectly valid program.

Reset the COMDAT group state in do_lto() so that the post-LTO symbol
resolution selects groups from a clean slate among the files that
actually remain in the link.

Fixes https://github.com/rui314/mold/issues/1565

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-26 10:37:15 +09:00
Rui Ueyama 4b00446c59 Support DTPREL relocations in non-allocated sections
Fixes https://github.com/rui314/mold/issues/1626
2026-07-26 00:10:27 +09:00
Rui UeyamaandClaude Fable 5 1d306dc0ac Intern symbols without a concurrent hash table
Like the previous commit did for comdat groups, symbol interning no
longer goes through a single concurrent hash table. Object file
parsing only records each global symbol name along with which slot of
the file's symbol vector it belongs to; gather_symbols() then
deduplicates the names into Symbol objects and fills in the files'
symbol pointers, processing each hash shard on a single thread with
no synchronization. Besides avoiding concurrent hash table
insertions, this hashes each name only once and no longer constructs
a temporary Symbol per lookup, which the concurrent map's insert
interface required. Linker-synthesized symbols, shared libraries and
linker scripts keep interning through a locked slow path in
get_symbol().

Linking a Chromium debug build on a Threadripper 7980X saves ~12
CPU-seconds (83 -> 71) and about 0.25 seconds of wall time at 32
threads. Output files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-20 15:29:27 +09:00
Rui UeyamaandClaude Fable 5 383ff33215 Deduplicate comdat groups without a concurrent hash table
We used to intern comdat group signatures into a single concurrent
hash table as object files are parsed. The concurrent map is costly
under 32-way insertion, and every file redundantly hashed the
signature strings, which are long mangled names for C++ programs.

Signatures are now handled by a new ShardedMap class: parsing only
appends them to thread-local buffers binned by hash, and once all
files have been read, each bin is deduplicated by a single thread
with no synchronization, filling in the files' ComdatGroupRefs. A
signature is hashed once, when it's recorded. Deduplication runs in
resolve_symbols() right before comdat elimination rather than at the
end of read_input_files() so that files parsed later, such as ELF
files produced by the LTO plugin, are covered when we redo symbol
resolution. LTO comdats otherwise take a mutex-protected slow path.

Linking a Chromium debug build on a Threadripper 7980X is about 4%
faster (3.22s -> 3.06s at 32 threads), saving ~4 CPU-seconds. Output
files are bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-20 15:07:11 +09:00
Rui UeyamaandClaude Fable 5 4ea8cb996f Read input files in parallel
We used to read input files one at a time in the command line order,
with a single loop opening and mmap'ing each file and walking archive
files. That loop, not parsing, is the bottleneck of the read phase;
for a Chromium debug build with ~45,000 input files, it takes ~1.3
seconds, about 40% of the total link time.

We now open and read files with a parallel for-each loop with the
feeder pattern; an archive file feeds one job per member. The command
line parser records the reader state (--as-needed etc.) and a
hierarchical position for each input file argument, and files are
sorted back into the command line order afterwards to assign
priorities. Linker scripts, which can mutate the linker context, are
parsed after the parallel loop, one at a time in the command line
order. LTO claims are serialized because the plugin interface has
shared state.

Linking Chromium debug on a Threadripper 7980X is ~2% faster at 32
threads and ~14% faster at 64 threads, where the serial loop
previously made additional threads a net loss. Output files are
bit-identical to before.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-20 11:37:47 +09:00
Rui UeyamaandClaude Fable 5 2d09378cfd Remove the use of mremap from LockingOutputFile
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-19 19:10:37 +09:00
Rui UeyamaandClaude Fable 5 a8fbf52ef1 Map the output file with room to grow instead of using mremap
mremap, which we used to grow the output file mapping for
.gdb_index, is Linux-specific, so commit 9b7c5267 broke the build on
the BSDs and macOS. Instead of growing a mapping that was created
with the exact file size, we now map the output file with twice as
much address space as its size upfront. The mapping beyond the end
of the file may not be touched, but growing the file with ftruncate
makes the tail accessible, so extend() no longer needs any mmap
call, and the output buffer never moves. Reserving address space
does not consume any physical memory; pages materialize only when
they are first touched.

This also fixes the madvise(MADV_HUGEPAGE) call in extend(), which
had always failed with EINVAL because its address was not
page-aligned. That went unnoticed because an mremap'ed mapping
inherits the huge-page flag from the original mapping.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-19 18:41:18 +09:00
Rui Ueyama 848135170a Update mold.1 (automated commit) 2026-07-19 05:50:51 +00:00
Rui UeyamaandClaude Fable 5 1c11d94c02 Allocate InputSections from a per-file pool
A large link creates tens of millions of InputSection objects, one
heap allocation each, and the profiler attributed 5% of all cycles
to their construction. Allocate them from a per-file std::deque
instead and refer to them through plain pointers. Section death is
represented by a null entry as before; the object itself stays in
the pool until the file is destroyed.

This removes the InputSection constructor from the profile and
cuts the allocation count by 4x, but the end-to-end link time is
unchanged within noise on a Chromium debug build; the constructor
cost was evidently not on the critical path. The change still pays
its way by making `sections` a plain pointer vector, which drops
the unique_ptr indirection and the .get() noise throughout.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-19 12:07:52 +09:00
Rui UeyamaandClaude Fable 5 45dea59990 Discard temporary local symbols by default
Compilers emit numerous temporary local symbols such as .L.str.42
for unnamed program elements. They are compiler-internal: nothing
references them through the symbol table, and DWARF refers to their
data by section offset. Nevertheless we kept them in the output by
default, following GNU ld. A Chromium debug build contains 2.26
million of them, and they added ~180 MiB of .symtab and .strtab to
the 4.9 GiB output.

Discard them by default, as lld does. The new --discard-none option
restores the previous behavior. -r and --emit-relocs continue to
keep all local symbols, since emitted relocations may refer to
them, and RISC-V and LoongArch no longer need their special case,
as the default now covers them.

Link time is unchanged within noise; the benefit is a ~4% smaller
output for large debug builds and a symbol table that no longer
resolves addresses to compiler-internal labels.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 23:32:56 +09:00
Rui UeyamaandClaude Fable 5 9b7c526704 Use mremap to extend the output file mapping
Previously, data appended to the output file, such as .gdb_index,
was staged in a heap buffer or a separate memory mapping. We can
instead just grow the existing mapping with mremap(MREMAP_MAYMOVE).
Consumers of the output buffer derive pointers from ctx.buf, so the
mapping is free to move, and since filesize now includes appended
data, the build ID and the .gnu_debuglink CRC naturally cover the
entire file.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 22:05:41 +09:00
Rui UeyamaandClaude Fable 5 a30c483ffa Parallelize section marking for __start_/__stop_ references
When a live section refers to a __start_<name> or __stop_<name>
symbol, every section named <name> must be kept alive. The garbage
collector marked those sections one by one in the thread that
found the reference. A single reference can fan out to an enormous
number of sections: ClickHouse is built with XRay instrumentation,
which emits one xray_instr_map section per function, and the XRay
runtime references __start_xray_instr_map, so one thread marked
and visited a couple hundred thousand sections while the rest of
the machine waited for the mark phase to finish.

This was a regression introduced by 384527fd: before it,
C-identifier-named sections were unconditional GC roots and
entered the root set through a parallel loop. That commit
correctly made their retention conditional on a reference to
their marker symbols but expanded the fanout serially. Keep the
precise semantics and spread the fanout over threads with a
nested parallel loop, storing the sections of each name as a
vector instead of entries of a multimap.

This shortens ClickHouse's debug link from 1.98s to 1.45s at the
default 32 threads; its gc pass drops from 0.56s to 0.09s.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 18:14:48 +09:00
Rui UeyamaandClaude Fable 5 d444584f2b Allocate ConcurrentMap tables with huge pages
ConcurrentMap allocated its hash table with MAP_POPULATE, which
zeroes every page of the mapping inside the mmap system call, on
one thread. The string-merge map for a Clang debug build spans
several gigabytes, so every other thread stalled for ~170ms at the
barrier between splitting and inserting strings while one thread
pre-faulted the table.

Map the table lazily and advise MADV_HUGEPAGE instead. Populating
the table on demand from concurrent inserts spreads the fault cost
over all threads, huge pages make each fault populate 2MiB rather
than a base page, and they also cut the TLB miss rate of the
random probes into the table. Explicit parallel pre-faulting was
measured to add nothing on top of this. This shortens a Clang
debug link from 2.13s to 1.85s and a Chromium debug link from
3.38s to 3.27s at the default 32 threads.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 13:44:55 +09:00
Rui UeyamaandClaude Fable 5 c8d44d73ce Write .gdb_index directly to the mmap'ed output file
.gdb_index was constructed in a malloc'ed buffer that close()
appended to the output with a single buffered write, copying the
entire section (534 MiB for a Chromium debug build) through the
page cache on one thread, into disk blocks that ext4 had to
allocate inside the write path.

Add OutputFile::extend, which returns a buffer for data appended
past the mapped image. A memory-mapped output file grows with
ftruncate, preallocates the new disk blocks, and memory-maps the
appended range, so write_gdb_index constructs the section directly
in the output file, in parallel. The range gets its own mapping
because growing the existing one with mremap could move it,
invalidating pointers into it held by the caller. Output files
that cannot be mapped (special files such as stdout, the separate
debug file, and Windows) keep buffering appended data on the heap,
now private to each implementation; the shared buf2 field is gone.

The fallocate calls are now guarded by a configure-time
HAVE_FALLOCATE check rather than #ifdef __linux__.

This shortens a Chromium debug link from 3.31s to 3.18s and makes
close_file effectively free.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 11:50:46 +09:00
Rui UeyamaandClaude Fable 5 97867c5354 Parallelize dynamic relocation collection
RelDynSection::update_shdr collected dynamic relocations by walking
all chunks serially, and a single output section such as
.data.rel.ro can account for most of an output's dynamic
relocations (~1.5 million for a Chromium debug build, mostly
R_RELATIVE for vtables). The walk computes each relocation's offset
and addend with Symbol::get_addr, which made it a ~0.1s
single-threaded stretch between parallel passes.

Process an output section's absolute relocations in fixed-size
parallel shards and flatten the per-shard results, so the resulting
section contents are identical to the serial version. This shortens
a Chromium debug link from 3.58s to 3.49s at the default 32
threads.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 10:57:56 +09:00
Rui UeyamaandClaude Fable 5 e2190e9a60 Avoid string allocations in command line option matching
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 09:08:22 +09:00
Rui UeyamaandClaude Fable 5 0a5bd29b56 Speed up command line option processing
parse_nonpositional_args tested each command line argument against
every option name, and each test allocated a few heap strings in
add_dashes. A command line for a large program consists almost
entirely of input file paths (a Chromium debug link passes ~19,000
paths but fewer than 100 options), so we were spending several
hundred milliseconds of serial time on failed option matches before
opening the first input file.

An argument that does not start with '-' cannot match any option,
so such arguments now go straight to the output list. This shortens
a Chromium debug link from 3.98s to 3.59s on a 64-core machine
because option processing delayed the start of the parallel parsing
phase.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 08:41:55 +09:00
Rui UeyamaandClaude Fable 5 be3d214c47 Fix SH4 dynamic relocations losing their addends
SH-4 object files store relocation addends in the relocated places
rather than in r_addend, and mold assumed that dynamic relocations
work the same way, emitting them with zero r_addend and the addend
written to the relocated place. The glibc dynamic linker, however,
follows the usual RELA convention: it reads the addend of a symbolic
dynamic relocation only from r_addend, and it reads the relocated
place only for R_SH_RELATIVE relocations whose r_addend is zero. Any
symbolic dynamic relocation with a non-zero addend thus lost its
addend at load time.

A typical victim is the vtable pointer of a typeinfo object in a
position-independent executable. It must point into libstdc++'s
vtable (e.g. _ZTVN10__cxxabiv117__class_type_infoE + 8), so its
dynamic relocation carries a non-zero addend. With the addend
dropped, virtual calls on such a typeinfo dispatch to wrong vtable
slots, which crashes the C++ exception machinery as soon as a
personality routine tries to match a catch clause. The icf-lsda test
caught this on sh4aeb because its CI toolchain creates PIEs by
default; the little-endian sh4 toolchain defaults to
position-dependent executables, in which the vtable is copy-relocated
and no dynamic addend is involved, so the same test passed there.

Remove the ElfRel specializations that zeroed addends for symbolic
relocations; the generic RELA record types have the identical layout
and keep addends. Relocation records emitted for -r or --emit-relocs
keep following the object-file convention, since their consumers
expect addends in the relocated places.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-15 18:10:26 +09:00
Rui UeyamaandClaude Fable 5 4ef13c9609 Speed up gdb-index construction
Creating a .gdb_index was slower than it should have been for three
reasons. read_pubnames_cu looked up compunits with a linear scan,
which made it quadratic in the number of compunits; the compunit
vector is sorted by offset, so a binary search suffices. gdb_hash
was computed for every name occurrence even though the same name
typically appears in many compunits; we now compute it only when a
name is inserted into the map for the first time. Finally, nametypes
were sorted with a stable sort, but their ordering is total, so a
plain sort produces the same result without the extra buffer.

On a Chromium debug build, this makes the write_gdb_index pass 2x
faster with --threads=1 (10.2s to 5.2s) and 28% faster at the
default 32 threads (0.69s to 0.50s). The output is bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-15 18:10:26 +09:00
Rui UeyamaandClaude Fable 5 3699cc68e2 Speed up version script symbol matching
Symbols are matched against version script patterns with an
Aho-Corasick automaton (for single-substring patterns) or an
NFA-based glob matcher, both of which scan the entire symbol name on
every query. But almost all patterns in real version scripts are a
match-all ("local: *;"), an exact name, or a name with one trailing
or leading "*", all of which can be matched with a plain string
comparison.

Handle such patterns directly and reserve the automatons for the
ones that actually need scanning, such as "*foo*" or "a*b".

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-15 18:10:26 +09:00
Rui Ueyama bfa7117757 Merge pull request #1624 from git-staus/main
Install build dependencies with `sudo`
2026-07-15 10:50:37 +09:00
git-staus 9bf01c1f1d Install build dependencies with sudo
The `README` gives users commands to copy, but `install-build-deps.sh`
is not ran as root, even though the CMake install step is indicated to
run with `sudo`. FreeBSD seems not to come with `sudo` by default, so
modifying the install script might be unwise. Simply place it in front
of the script invocation itself in the `README`.
2026-07-15 02:10:05 +02:00
Rui UeyamaandClaude Fable 5 5ba31dc3ea Clean up ICF
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 22:34:18 +09:00
Rui UeyamaandClaude Fable 5 86b42639cc Remove the affinity partitioner from the ICF propagation loop
The affinity partitioner replayed each round's chunk-to-thread
placement so that a thread would re-read the digests it wrote in the
previous round from its own cache. That no longer pays off: after the
recent ICF rework, the propagation loop streams over the digest and
edge arrays and is bound by memory bandwidth rather than cache reuse.

Linking Chromium on a Threadripper 7980X (64 cores, 8 CCDs), the
propagate timer is unchanged at 16 and 128 threads and loses only
about 2 ms (4%) at 32 threads, which is invisible in the total link
time. In exchange, the default auto partitioner retains full work
stealing, which balances better against the skewed per-section edge
counts, and the partitioner no longer needs to be threaded through
two function signatures.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 21:59:50 +09:00
Rui UeyamaandClaude Fable 5 a06b6234c9 Fix ICF folding functions with different exception tables
compute_digest hashes an eligible relocation target without identity,
relying on a graph edge to keep different targets distinguishable
through the propagation rounds. But gather_edges only created edges
for a section's own relocations, not for the relocations in its FDEs,
so the reference from an FDE to its LSDA never became an edge. As a
result, two functions with identical machine code whose exception
tables catch different types were considered identical, and folding
them changes which exceptions the survivor catches. The bug is as old
as mold's ICF and also affects --icf=safe, since such functions are
usually not address-taken.

Create edges for FDE relocations, mirroring exactly what
compute_digest hashes. The included test, two byte-identical
functions catching different exception types, aborts with an uncaught
exception without this fix.

On 32-bit ARM the equivalent bug still exists through a different
mechanism: exception tables are .ARM.extab sections tied to functions
via .ARM.exidx rather than FDE references, which ICF does not yet
track. The test skips ARM for now.

A Chromium debug build is unaffected (same rounds, same number of
folded sections) since Chromium builds with -fno-exceptions.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:11 +09:00
Rui UeyamaandClaude Fable 5 6b5df4d6cf Use sentinel-terminated index arrays in ICF
gather_sections and gather_edges each kept a per-element count array
next to a starting-index array and combined the two at the end.
Counting directly into the index array shifted by one and running the
prefix sum in place removes the count arrays, and the extra entry at
the end means edge_indices[i + 1] is valid for every vertex, so the
propagation loop no longer needs to special-case the last vertex to
find where its edge list ends. The empty-input early return in
gather_edges falls out as well.

No behavior change.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:11 +09:00
Rui UeyamaandClaude Fable 5 3e62b432a5 Compute ICF digests from the previous round's digests
A propagation round used to compute a vertex's next digest by hashing
its initial digest together with its neighbors' current digests,
which required keeping three digest arrays: the initial one and two
that alternate between current and next.

Hash the vertex's current digest instead of its initial one. This is
the textbook formulation of iterated partition refinement, and it
distinguishes exactly the same vertex pairs in every round: a current
digest determines the initial digest injectively (up to hash
collisions, which the whole algorithm already assumes away), so the
per-round partitions, the number of rounds and the final equivalence
classes are all unchanged.

The initial digest array disappears, saving 16 bytes per vertex, and
the current/next arrays are now two plainly named vectors that swap
after each round instead of an indexed triple with a flip-flop slot
index.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:11 +09:00
Rui UeyamaandClaude Fable 5 f478001cc0 Remove per-vertex convergence tracking from ICF
The propagation rounds used to record which vertices' digests had
stopped changing in a `converged` bitmap and skip rehashing them in
later rounds. That was worthwhile when rounds were expensive, but by
now a round is a cheap, uniform data-parallel pass, and the
bookkeeping costs as much as it saves: reading and comparing two
digests per vertex per round just to skip a short hash computation.

Rehash every vertex on every round instead. The convergence check was
the last user of Digest's equality operator, so that is gone too.

For a Chromium release build, the whole ICF pass got slightly faster
(0.166s to 0.152s wall, 4.7s to 4.3s of CPU time).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:11 +09:00
Rui UeyamaandClaude Fable 5 7172b136c9 Remove ICF leaf premerging
Leaf sections, those with no outgoing references, used to be merged
upfront so that they don't have to participate in the propagation
rounds as graph vertices. That was worth the code when the rounds and
the convergence check were expensive, but both have since become
cheap, and the propagation machinery handles leaves naturally: a
leaf's digest never changes, so it is marked as converged after the
second round and costs almost nothing from then on.

This deletes merge_leaf_nodes, is_leaf and the icf_leaf flag, and
moves eligibility classification to gather_sections. The leader
branch in hash_symbol is gone too because no section has a leader
anymore by the time digests are computed.

Also tidy up the neighborhood: the counting pass of gather_edges
skipped symbols that point to a section fragment before asking for
their input section, but a symbol's origin is a tagged pointer that
is either a fragment or an input section, so get_input_section()
already returns nullptr for fragment symbols. Dropping the redundant
check makes the counting pass and the filling pass identical in
shape.

For a Chromium release build, the ICF pass takes the same time as
before; the propagation rounds absorb roughly what merge_leaf_nodes
used to cost. The graph gets about 30% more vertices, which costs
some transient memory.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:11 +09:00
Rui UeyamaandClaude Fable 5 c6f436690b Represent ICF digests as a pair of 64-bit words
A digest used to be std::array<uint8_t, 16>, so DigestMap had to
memcpy the bytes into 64-bit words before deriving the slot index and
tag from them. Defining Digest as a struct of two u64 members gives
the words names and makes the copies disappear.

icf.cc is compiled once per target, so the struct is defined in every
one of those translation units. The definitions are all identical, so
this is fine under the one-definition rule, just as the old type
alias was.

No behavior change.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:51:03 +09:00
Rui UeyamaandClaude Fable 5 923df2f0d6 Merge ICF leaf sections by digest
Leaf sections used to be merged with a dedicated hash map keyed by
raw section contents, with its own hasher and equality functor. That
equality was subtly weaker than the one the propagation rounds use:
it compared FDE bodies but ignored which CIE each FDE points to, so
two identical functions whose CIEs recorded different personality
routines could be merged incorrectly. It also ignored section flags.

Instead, compute the same digest for leaves that compute_digest
produces for graph sections and merge leaves through DigestMap, the
same structure the propagation rounds use. One definition of section
equality now covers both paths, which fixes the personality bug, and
LeafHasher, LeafEq and the last digest-keyed use of tbb's hash map in
this file disappear.

The trade-off is that leaf merging now relies on 128-bit digest
uniqueness instead of comparing contents exactly, but that is the
same assumption the rest of ICF is already built on.

For a Chromium debug build linked with --icf=all, merge_leaf_nodes
went from 0.07s to 0.10s because a leaf's contents are now hashed by
SipHash twice, once to insert and once to look up. 152 of 3.6M folded
sections are no longer merged; those are the sections the weaker
equality was conflating.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 20:49:55 +09:00
Rui UeyamaandClaude Fable 5 b922536782 Fold ICF leader election into digest counting
To group sections at the end of ICF, we used to build a concurrent
hash map from digest to the lowest-priority section holding it, which
duplicated most of what the digest-counting hash set already does.
That map was also slow enough to construct and destruct that we
deferred freeing it until exit.

This patch generalizes the counting set into DigestMap by adding a
section pointer to each slot. The counting rounds now elect the
lowest-priority section for each digest as a side effect, so by the
time the propagation loop converges, the final round has already
elected the leader of every equivalence class, and the grouping step
reduces to a single lookup pass. The digest-keyed tbb map and its
std::hash specialization disappear, and so does ctx.on_exit, whose
only purpose was to defer freeing that map.

For a Chromium debug build linked with --icf=all, the grouping pass
went from 0.23s to 0.01s and the propagation loop got 0.11s slower
for maintaining the leaders, making the whole ICF pass about 10%
faster (0.89s to 0.79s). The ICF fold decisions are unchanged.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 16:39:26 +09:00
Rui UeyamaandClaude Fable 5 22a8d49788 Count distinct ICF digests on every propagation round
To detect convergence of the ICF propagation rounds, we count the
number of distinct digests and stop when it ceases to increase. We
previously gated the count on a cheaper necessary-looking condition:
as long as the number of changed digests per round was still moving,
we assumed the graph could not have converged and skipped counting.

That assumption is wrong. A changing digest does not imply a changing
partition, because sections in the same equivalence class change
their digests in lockstep. For example, in a chain of sections that
are all distinct from the start, the partition is final after the
first round, but the changed-digest count keeps moving until the
propagation depth reaches the chain length, so the gate would delay
termination by the depth of the chain.

Since counting became cheap, we can simply count on every round and
stop as soon as the count repeats, which is the earliest round at
which convergence is provable. propagate() no longer needs to count
changed digests and report them to the caller.

For a Chromium debug build linked with --icf=all, this reduces the
number of propagation rounds from 40 to 28. The 12 saved rounds are
ones in which most sections had already converged, so the wall clock
time is unchanged. The output file is bit-identical.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-14 15:34:46 +09:00
Rui Ueyama 1ef1ea49ed Simplify 2026-07-14 15:29:47 +09:00
Rui Ueyama 0a04fb22ad Upgrade bundled mimalloc to v3.3.2 2026-07-13 19:59:18 +09:00
Rui UeyamaandClaude Fable 5 5f2d627597 Count distinct ICF digests with a hash set instead of sorting
To detect convergence of the ICF propagation rounds, we count the
number of distinct digests and stop when it ceases to increase. We
previously obtained the count by copying all digests and running a
parallel sort, which was expensive enough that we ran it only once
per 10 propagation rounds, overshooting the convergence point by up
to 9 rounds plus a full confirmation batch.

This patch replaces the sort with a concurrent open-addressing hash
set. Since the number of distinct digests cannot exceed the number of
digests, the table is allocated once at full size and never grows.
Slots are stamped with the round number, so the table needs no
clearing between rounds; and a slot match effectively compares an
entire 128-bit digest, so the count is exact under the same
hash-collision assumption the algorithm is already built on. Counting
is now cheap enough to run on every round that passes the existing
changed-digest gate, so the loop exits at the earliest round at which
convergence is provable, and the batching heuristic is gone.

For a Chromium debug build linked with --icf=all, the propagation
loop got 2.2x faster (0.73s to 0.34s), the number of propagation
rounds dropped from 59 to 40, and the whole ICF pass went from 1.29s
to 0.90s. The output file is unchanged.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-13 16:04:33 +09:00
Rui Ueyama f485f44d06 Update the MADV_HUGEPAGE comment
madvice to enable transparent hugepages works only on ext4 on recent
Linux kernels.
2026-07-11 14:14:23 +09:00
Rui Ueyama a708f03e9a Fix Windows builder 2026-07-10 18:54:55 +09:00
Rui UeyamaandClaude Fable 5 b126f83271 Ask for transparent huge pages for large mimalloc allocations
This changes the bundled mimalloc.

mimalloc calls madvise(MADV_HUGEPAGE) on suitable memory regions only
when the caller also allows large OS pages, i.e. hugetlbfs pages.
That condition makes sense for hugetlbfs, whose pages are physically
committed at map time and thus shouldn't back lazily-committed
segments, but transparent huge pages have no such cost because their
pages materialize on fault. Since both were gated on the same flag,
huge mimalloc objects, which are allocated through a code path that
never sets the flag, were never eligible for THP under the madvise
THP mode that Linux distributions ship by default. mold's largest
data structures, such as the symbol and relocation arrays, are
exactly such objects; several gigabytes of them were backed by 4 KiB
pages.

This patch makes the THP advise depend only on the allow_thp option
(enabled by default except on Android) and leaves the hugetlbfs gate
as is. mimalloc v3 effectively behaves this way already; only the v2
line has this problem.

Linking a Chromium debug build gets about 2% faster in the default
fork mode (3.37s to 3.29s) and 15% faster with --no-fork (4.05s to
3.43s) on our machine, as the heap becomes almost fully backed by
huge pages, which also makes the process exit much cheaper.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 07:28:34 +09:00
Rui UeyamaandClaude Fable 5 0086e97e8d Drop page table entries in parallel before exit
The kernel reclaims an exiting process's address space in a single
thread, which takes an appreciable amount of time for a process that
maps tens of gigabytes of input files. We now drop the page table
entries for input file mappings with madvise(MADV_DONTNEED) on all
threads before exiting. File contents stay in the page cache, so warm
rebuilds are unaffected.

This makes linking a Chromium debug build with --no-fork about 11%
faster (4.55s to 4.05s) on our machine. Without --no-fork, the
teardown happens in the detached child process, which now releases
its memory sooner.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 07:10:15 +09:00
Rui UeyamaandClaude Fable 5 e57c733e74 Re-enable transparent huge pages if a parent process disabled them
The PR_SET_THP_DISABLE prctl flag is inherited across fork and exec,
so a linker spawned from a process that has disabled transparent huge
pages for itself also runs without them, no matter what the system
THP setting says. mold allocates on the order of 10 GiB of heap for a
large link, and backing it with 4 KiB pages instead of 2 MiB pages
makes linking nearly twice as slow (6.2s vs 3.4s for a Chromium debug
build on our machine), so clear the inherited flag at startup.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 07:09:28 +09:00
Rui Ueyama 1adc707d49 Add unique_comdats statistic counter 2026-07-08 16:52:09 +09:00
Rui Ueyama 2ad8219352 Fix dist.sh 2026-07-08 16:37:03 +09:00
Rui Ueyama c0b29dc05e Upgrade bundled blake3 to 1.8.5 2026-07-03 10:18:38 +09:00
Rui Ueyama 2b340d400b Upgrade bundled zlib to 1.3.2 2026-07-03 10:15:29 +09:00
Rui Ueyama 7f75a1adde Upgrade bundled OneTBB to 2023.0.0 2026-07-03 10:13:15 +09:00
Rui Ueyama 930bb45339 Upgrade bundled mimalloc to 2.3.2 2026-07-03 10:11:21 +09:00
Rui Ueyama 36e80ccea5 Fix CI 2026-07-03 09:06:24 +09:00
Rui Ueyama 0ddff68d52 Merge pull request #1618 from agrieve/as-needed-2
Remove DSOs used only by GC'ed sections
2026-07-03 09:00:52 +09:00
Andrew Grieve 3d546cd068 Remove DSOs used only by GC'ed sections
lld does this by default. See: https://crbug.com/529102889

If a DSO is only referenced by sections that are garbage-collected, the
DSO's DT_NEEDED entry will be omitted.
2026-07-02 15:59:06 -04:00
Rui Ueyama f5fd48874a Revert recent changes that upgraded distros for ./dist.sh
These changes broke cross builds for ppc64le and s390x. I couldn't fix
it easily. Every fix attempt also takes a long time because of slow
qemu-user emulation.

I'll revert CMakeLists.txt and dist.sh to the original state. I don't
think we need to upgrade distros used by dist.sh as long as the binaries
produced using these images work.
2026-07-01 14:38:21 +09:00
Rui Ueyama 6b7e8d86e2 Run test scripts directly instead of via bash -x
This ensures that all test files have x bit.
2026-07-01 11:40:45 +09:00
Rui Ueyama aa50e9f88d Simplify
Now our tar version is recent enough to use the `--sort` option.
We still need to run gzip explicitly in the pipeline because we want
to pass -n (--no-name) to not save the current time as the file
timestamp in a gzip header.
2026-06-30 17:59:09 +09:00
Rui Ueyama 26ccd7aecc Simplify 2026-06-30 17:18:35 +09:00
Rui Ueyama 6b1914d062 Simplify 2026-06-30 17:08:09 +09:00
Rui Ueyama 3bb16732ae Merge pull request #1612 from a5ehren/mold_container
build: update dist.sh by refreshing all distro images
2026-06-30 16:43:19 +09:00
Rui Ueyama 81ccee4446 Merge pull request #1615 from marxin/test-permissions-2 2026-06-30 11:35:09 +09:00
Martin Liska dc5c6c5fa4 chore(tests): add missing +x for a test 2026-06-29 23:04:55 +02:00
Ehren Bendler f07bf7352f build: update dist.sh by refreshing all distro images
* update x86-64 to Debian 10. This has glibc 2.28 which matches the oldest actually used distro these days (rhel 8) and gcc-8.5 is new enough to bootstrap clang and libc++, which saves the whole gcc build step
* set the riscv64 target to use debian Trixie, which was the first stable release for that arch
* bullseye (arm,ppac,s390), trixie (riscv), and the loongarch sid snapshot all have clang-19 available
* make sure all images install the libc++-19-dev package to get libc++ static version
2026-06-29 13:21:20 -04:00
Ehren Bendler 2ec5e72691 build: add convienence flags for selecting the C++ stdlib on Linux
* MOLD_USE_LIBCXX now configures the build to look for and use llvm's libc++. This is already the default on macOS and BSD
* Added MOLD_STATIC_* flags for several libs to give more config beyond the overall MOLD_MOSTLY_STATIC
2026-06-29 13:15:49 -04:00
Rui Ueyama f4a62c7503 Fix CI 2026-06-16 17:40:10 +09:00
Rui Ueyama 075c63861d Simplify 2026-06-16 16:46:04 +09:00
Rui Ueyama 94c259a89a Merge pull request #1609 from sunyuechi/fix-empty-default-symver
Don't let "local: *" hide a foo@@ default-versioned symbol
2026-06-16 16:14:16 +09:00
Rui Ueyama 8b79fbbf02 Attempt to fix buildbot 2026-06-16 15:08:44 +09:00
Rui UeyamaandClaude Opus 4.8 ad4c489fff Rewrite relaxed relocation types for --emit-relocs
When mold relaxes an instruction, the relocation attached to it no
longer matches the rewritten code. With --emit-relocs that mismatch is
written to the output and confuses consumers such as BOLT. This change
addresses it by rewriting each emitted relocation to the type that
matches the relaxed instruction.

RISC-V and LoongArch already did this in a separate rewrite_reloc_types()
pass that could drift from apply_reloc_alloc; fold it into the latter and
delete the pass. ARM64, x86-64, i386 and s390x did not rewrite types
and now do, e.g. x86-64 GOTPCRELX => PC32, i386 GOT32X => GOTOFF, s390x
GOTENT => PC32DBL, ARM64 GOT loads => ADR_PREL_PG_HI21 and TLSDESC =>
IE/LE. Markers that GNU ld also keeps, such as x86-64 TLS and sparc64
GOTDATA_OP, are left unchanged.

Fixes https://github.com/rui314/mold/issues/1610

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-16 14:12:08 +09:00
Sun Yuechi 71cbd79f8c Don't let "local: *" hide a foo@@ default-versioned symbol
A symbol exported with an empty default version (foo@@) was treated as a
plain unversioned symbol, so a version script's "local: *" wildcard
localized it and dropped it from the dynamic symbol table; GNU ld keeps
it exported. Route foo@@ through parse_symbol_version() and assign it
VER_NDX_GLOBAL so it overrides the wildcard.

Signed-off-by: Sun Yuechi <[email protected]>
2026-06-15 19:49:36 +08:00
Rui UeyamaandClaude Fable 5 85c115e1be Sign-extend in-place addends of i386 relocations
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-11 11:31:37 +09:00
Rui UeyamaandClaude Opus 4.8 d0aa95abe9 Support the SFrame v3 stack-unwinding format
SFrame is a compact alternative to .eh_frame used for asynchronous stack
unwinding by profilers and debuggers. As of binutils 2.46, gas emits
.sframe (Version 3) by default with -Wa,--gsframe, so object files now
routinely carry it. Like .eh_frame, .sframe can't be treated as opaque
bytes: an output section is a single header followed by a PC-sorted index
of Function Descriptor Entries (FDEs) and a blob of Frame Row Entries
(FREs), so the linker has to parse the inputs and rebuild it.

This adds SFrame Version 3 support. We parse each input .sframe into its
FDEs, drop the entries for garbage-collected or ICF-folded functions,
concatenate the survivors, sort the index by function address and rewrite
the header. The FRE data carries no relocations and is position
independent, so it is copied verbatim; only each FDE's func_start is
relocated, which we resolve to a PC-relative offset (the output always
sets SFRAME_F_FDE_FUNC_START_PCREL). A PT_GNU_SFRAME segment is emitted so
the runtime can find the section.

For a relocatable output we can't resolve addresses yet, so the merged
index is left unsorted and each func_start is emitted as a relocation in
.rela.sframe for the final link to resolve, matching GNU ld -r.

We support only Version 3. A .sframe with the wrong magic is reported as
an error, whereas a section written in an older version is ignored.

Fixes https://github.com/rui314/mold/issues/1511

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-09 20:16:15 +09:00
Rui Ueyama eb273d1c1c Refactor 2026-06-07 19:58:57 +09:00
Rui Ueyama 3e3687e64e Merge pull request #1577 from bamo/fix/no-interp-on-shared
Don't emit PT_INTERP for -shared outputs
2026-06-07 19:37:50 +09:00
Rui Ueyama 8f598a0f96 Add a missing dependency 2026-06-07 14:25:14 +09:00
Rui Ueyama 6a2f747bf9 Fix -Wformat-truncation 2026-06-07 14:25:03 +09:00
Rui Ueyama 9c24d197c1 Merge pull request #1597 from minhbq-99/fix-align
Fix PT_LOAD alignment issue
2026-06-07 12:55:57 +09:00
Rui UeyamaandClaude Opus 4.8 5beb02714c Increase thunk batch size and tie max_thunk_size to it
Large C++ debug libraries on ARM64 (e.g. deal.II's libdeal_II.g.so) can
have a single batch reference enough distinct PLT-routed DSO imports that
the first-pass thunk group exceeds the old fixed 1 MiB max_thunk_size,
tripping the assertion in create_range_extension_thunks().

Replace the branch_distance/5 formula with explicit per-target batch
sizes (32/8/16 MiB for ARM64/ARM32/PPC) and derive max_thunk_size from
the batch as batch_size/2 (16/4/8 MiB). The reservation in d_thunk_end()
already accounts for max_thunk_size, so a larger cap is safe by
construction, and batch_size/2 is about the largest reservation ARM32's
16 MiB branch range can accommodate without throttling the batch.

Add a regression test that calls 50,000 imported functions from a single
function, which on ARM64 produces a first-pass thunk group well over the
old 1 MiB limit.

Fixes https://github.com/rui314/mold/issues/1538

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-07 00:20:34 +09:00
Rui UeyamaandClaude Opus 4.8 4afb95ac5b Support SPARC64's large PLT format
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-07 00:20:30 +09:00
Rui UeyamaandClaude Opus 4.8 2d45a8eab7 Make the HWASAN test work with older Clang
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 19:49:37 +09:00
Rui UeyamaandClaude Opus 4.8 a0c46e95d3 Support R_AARCH64_MOVW_PREL_G* relocations
Fixes https://github.com/rui314/mold/issues/1592

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 18:50:54 +09:00
Rui UeyamaandClaude Opus 4.8 2a90660aa0 Fix invalid artifact name for distros containing / or :
actions/upload-artifact rejects names with a forward slash or colon, so
the test-results upload failed for the opensuse/tumbleweed matrix entry
(and would have failed for gentoo/stage3, debian:11, and the ubuntu:*
entries on a test failure). Sanitize matrix.distro by mapping / and :
to - before using it as the artifact name.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 18:13:22 +09:00
Rui UeyamaandClaude Opus 4.8 f147d78a56 Make --emit-relocs reflect relaxed instructions on RISC-V and LoongArch
After relaxation, the relocations attached to the relaxed instructions no
longer match the new, shorter instructions. With --emit-relocs we copied the
input relocations verbatim, emitting stale types at stale offsets.

Rewrite each relocation's offset and type in RelocSection::copy_buf to match
the relaxed code, recomputing the outcome the same way apply_reloc_alloc
recomputes its instruction rewrites. -r output is not relaxed, so its
relocations are passed through unchanged.

Fixes https://github.com/rui314/mold/issues/1584

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 17:31:57 +09:00
Rui Ueyama aa52f90d69 Simplify 2026-06-06 13:15:32 +09:00
Rui UeyamaandClaude Opus 4.8 7a66f71796 Set sh_entsize and sh_addralign correctly for Android packed relocs
The Android packed relocation section (SHT_ANDROID_RELA/REL) holds a
variable-length SLEB128-encoded delta stream, not a table of fixed-size
entries, so sh_entsize should be 0 and the byte stream needs no word
alignment. We were inheriting sizeof(ElfRel) and sizeof(Word) from the
plain .rela.dyn path. These fields aren't consulted at runtime (bionic
locates the blob via DT_ANDROID_RELA/RELASZ), so this only corrects the
metadata and lets the section pack without spurious padding.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-06 12:52:17 +09:00
Rui Ueyama 2708ba6201 Fix LTO COMDAT owner selection race 2026-06-06 12:52:17 +09:00
Rui Ueyama 400dfad97c Fix Android packed reloc size oscillation
Fixes https://github.com/rui314/mold/issues/1595
2026-06-06 12:52:17 +09:00
Rui Ueyama 6841e77230 Merge pull request #1568 from gruenich/feature/update-github-actions
[ci] Update GitHub Actions to their latest major releases
2026-06-05 15:48:38 +09:00
Rui Ueyama e9c3d1629f Merge pull request #1599 from agrieve/icf-plt
icf: Do not fold relocations pointing to preemptible symbols
2026-06-04 21:33:52 +09:00
Rui Ueyama b91f3363e1 Merge pull request #1600 from agrieve/icf-alignment
icf: Merge alignments of folded sections
2026-06-04 16:17:22 +09:00
Andrew Grieve 899655b2c1 icf: Merge alignments of folded sections
When folding identical sections, ICF was discarding the alignment of the
folded sections and only keeping the alignment of the chosen leader. If
a section with stricter alignment (e.g. 4-byte) was folded into a
section with looser alignment (e.g. 1-byte), the folded function could
be placed at an odd address.

On x86/x86_64, member function pointers use the lowest bit of the
address for virtual/non-virtual distinction. If a non-virtual member
function is folded and placed at an odd address, it is incorrectly
interpreted as virtual, leading to crashes. This fix ensures that we
update the leader's alignment to be the maximum alignment of all folded
sections. We do this in a global pass over all sections (both leaf and
non-leaf merged sections) before the sweep phase.

Fixes #1596
2026-06-03 15:13:31 -04:00
Andrew Grieve 17e705efaf icf: Do not fold relocations pointing to preemptible symbols
If a relocation points to a preemptible (imported) symbol, it should not
be considered identical to a relocation pointing to a local symbol, even
if their targets currently have identical bodies. Preemptible symbols
can be overridden at runtime, so we must preserve their unique identity.

Fixes #1598
2026-06-03 12:45:16 -04:00
Bui Quang Minh ad4a683ff6 Fix PT_LOAD alignment issue
With a PT_LOAD segment, we need to make sure p_offset % p_align ==
p_vaddr % p_align. In set_file_offsets, we try to assign contiguous file
offsets when sections are contiguous in memory. But when the alignment
of section changes and it's bigger than page size, we cannot do that.
The offset might need to be adjusted to follow the alignment rule.

A test binary with
__attribute__((section(".sum"))) __attribute__((aligned(8192))) void sum() {}

Section Headers:

[16] .plt.got          PROGBITS        00000000000015c0 0005c0 000008 00  AX  0   0 16
[17] .sum              PROGBITS        0000000000002000 001000 00000b 00  AX  0   0 8192

.sum section is assigned contiguous offset to its previous section. As a
result, in create_phdr, .sum is mapped to the same PT_LOAD segment as
previous sections.

Program Headers:

LOAD           0x000588 0x0000000000001588 0x0000000000001588 0x000b80 0x000b80 R E 0x2000

p_offset = 0x588
p_vaddr = 0x1588
p_align = 0x2000

So p_offset % p_align != p_vaddr % p_align, it violates the alignment
rule. As a result, if this scenario happens to a shared library, it
cannot be dlopen() on a system with glibc < 2.35 which is before this
commit:
https://github.com/bminor/glibc/commit/163f625cf9becbb82dfec63a29e566324129c0cd.

Fix it by not assigning contiguous offset when section's alignment is
different and it's bigger than page size.
2026-06-02 22:08:51 +07:00
Christoph Grüninger d557daa268 [ci] Update GitHub Actions to their latest major releases
Fixes most deprecation warnings that Node20.js will stop working
in June.
2026-05-30 22:02:11 +02:00
Rui Ueyama 4f08de1147 Simplify 2026-05-20 20:53:55 +09:00
Rui Ueyama bdb00bc558 Simplify 2026-05-20 20:27:15 +09:00
Bennett Amodio abcbd451b4 perf: cache symbol names to avoid per-call strlen
Several parse-time sites converted `symbol_strtab.data() + st_name`
implicitly to string_view, running strlen on each call. Precompute
a string_view vector parallel to elf_syms and read from it.
2026-05-20 19:40:13 +09:00
Bennett Amodio f1b4be87f6 perf: cache InputSection name to avoid per-call strlen
InputSection::name() implicitly converts a `const char *` to
string_view, which calls strlen on the shstrtab entry at every call.
Precompute the name as a string_view once in the constructor.

Linking clang Debug is ~1.3% faster; mlir-opt Debug is ~2.6% faster.
2026-05-20 19:40:09 +09:00
Rui Ueyama 441f231dbf Revert "perf: cache symbol names to avoid per-call strlen"
This reverts commit 5f272c4184 as it
broke tests.
2026-05-20 10:22:36 +09:00
Rui Ueyama 1f44c6c6df Revert "perf: cache InputSection name to avoid per-call strlen"
This reverts commit c32946dfab as it
broke tests.
2026-05-20 10:22:25 +09:00
Rui Ueyama 154e99b2ff Merge pull request #1588 from bamo/perf/cache-symbol-names
perf: cache symbol names to avoid per-call strlen
2026-05-20 10:03:35 +09:00
Rui Ueyama 048b7b2dfd Merge pull request #1587 from bamo/perf/cache-input-section-name
perf: cache InputSection name to avoid per-call strlen
2026-05-20 09:56:50 +09:00
Rui Ueyama 8b5eeb84fa Merge pull request #1589 from bamo/perf/concurrentmap-prefault
perf: pre-fault ConcurrentMap pages with MAP_POPULATE
2026-05-20 08:54:55 +09:00
Rui Ueyama 3c018ca56f Merge pull request #1586 from andreas-schwab/main
Fix ambiguous readelf option in gdb-index-dwarf5 test
2026-05-20 08:07:14 +09:00
Bennett Amodio 4a9e30defd perf: pre-fault ConcurrentMap pages with MAP_POPULATE
ConcurrentMap::insert reads ent.key before writing, so on an empty
slot the read first installs a PTE pointing at the system zero page,
then the CAS triggers wp_page_copy and a cross-CPU TLB shootdown.
Under heavy parallel insertion this shows up as ~3% of link time in
smp_call_function_many_cond plus a much larger inclusive cost in
asm_exc_page_fault.

Pass MAP_POPULATE to pre-fault the whole map in the mmap() thread.
Reads no longer install zero-page PTEs and writes no longer trigger
CoW. Linking clang-23 Debug is ~5% faster, llvm-tblgen ~7%,
clang-tblgen ~9%; max RSS rises by 0.1-0.6%. Falls back cleanly on
platforms that don't define MAP_POPULATE.
2026-05-19 14:09:59 -07:00
Bennett Amodio 5f272c4184 perf: cache symbol names to avoid per-call strlen
Several parse-time sites converted `symbol_strtab.data() + st_name`
implicitly to string_view, running strlen on each call. Precompute
a string_view vector parallel to elf_syms and read from it.
2026-05-19 09:58:36 -07:00
Bennett Amodio c32946dfab perf: cache InputSection name to avoid per-call strlen
InputSection::name() implicitly converts a `const char *` to
string_view, which calls strlen on the shstrtab entry at every call.
Precompute the name as a string_view once in the constructor.

Linking clang Debug is ~1.3% faster; mlir-opt Debug is ~2.6% faster.
2026-05-19 08:34:17 -07:00
Andreas Schwab e77e9d52b8 Fix ambiguous readelf option in gdb-index-dwarf5 test
The binutils readelf command now accepts a --debug-dir=<DIR> option which
makes --debug=gdb_index ambiguous.
2026-05-19 17:06:06 +02:00
Rui Ueyama a2e670e2a4 Merge pull request #1581 from marxin/test-permissions
chore(tests): add missing +x for 2 tests
2026-05-18 10:20:07 +09:00
Rui UeyamaandClaude Opus 4.7 019fb43e8c Simplify test
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-18 10:14:50 +09:00
Rui UeyamaandClaude Opus 4.7 61933bfd79 Fix relative response file paths failing after -C on emulation redo
Commit 6d6a128b moved the -C chdir(2) into parse_nonpositional_args(),
which is invoked by both the speculative X86_64 mold_main() and the
re-run via redo_main(). When -C is in effect, the second invocation
re-entered expand_response_files() from the post-chdir directory, so
@rsp with a relative path could no longer be opened.

Save the cwd before parsing and restore it before redo_main() so the
re-run resolves relative paths from the original directory. Also
switch the chdir call itself to std::filesystem::current_path().

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-18 10:13:42 +09:00
Rui Ueyama b4bb6a856d Merge pull request #1579 from bamo/fix/gdb-index-rnglistx
--gdb-index: use DW_FORM_rnglistx index instead of iterating all offset entries
2026-05-18 10:12:02 +09:00
Rui Ueyama b7bbf9c15c Merge pull request #1580 from bamo/fix/msys-windows-include
Include <windows.h> directly in mold.h
2026-05-18 10:11:29 +09:00
Martin Liska a3affe8789 chore(tests): add missing +x for 2 tests 2026-05-13 22:05:58 +02:00
Bennett Amodio 87b2889254 Include <windows.h> directly where Windows APIs are used
src/mold.h (HANDLE in MappedFile) and lib/perf.cc (FILETIME /
GetProcessTimes / GetCurrentProcess) both use Windows APIs inside
_WIN32 blocks but neither included <windows.h>. The MSYS2 UCRT64 build
worked because <tbb/enumerable_thread_specific.h> (pulled in via
lib/lib.h) used to #include <windows.h> for its TLS code.

oneTBB 2023.0.0 replaced that bulk include with forward declarations
of just the TLS functions ("declare prototypes of just the needed TLS
functions, instead of including whole windows.h"). When MSYS2 picked
up 2023.0.0, build-msys started failing with "'HANDLE' does not name
a type" at src/mold.h:224 (and the same shape of error in lib/perf.cc
after the mold.h fix landed).

Include <windows.h> explicitly in both translation units so the build
doesn't rely on transitive paths through third-party headers.

Last passing run: https://github.com/rui314/mold/actions/runs/25585325507/job/75112599227
First failing run: https://github.com/rui314/mold/actions/runs/25767054228/job/75681837767
2026-05-13 10:24:06 -07:00
Bennett Amodio 70ca8ffa53 --gdb-index: use DW_FORM_rnglistx index instead of iterating all offset entries
A compile unit's DW_AT_ranges attribute in DW_FORM_rnglistx form holds an
index into the offset table at DW_AT_rnglists_base; the corresponding
entry locates the range list for that DIE. mold was ignoring the index
and iterating every offset, picking up range lists belonging to child
DIEs (subprograms, lexical_blocks, inlined_subroutines) that use
DW_FORM_rnglistx as well.

The resulting .gdb_index address table contained child-DIE ranges nested
inside the CU's range. GDB 17 detects the overlap, prints "warning:
.gdb_index address table has a range (...) that overlaps with an earlier
range, ignoring .gdb_index", and discards the index. Older GDBs accept
the bloated table without complaint -- the duplicate entries all point
to the same CU, so lookups still resolve correctly -- which is why this
went undetected for years.

The bug only triggers when a CU contains a non-contiguous lexical_block,
which clang emits for scopes with EH landing pads from throwing RAII
destructors. Replace the loop with offsets[ranges.value], matching the
DW_FORM_sec_offset branch and GDB's own read_rnglist_index.

The new test invokes clang++ directly since DW_FORM_rnglistx is a
clang-specific behavior; gcc through 13 uses DW_FORM_sec_offset, which
mold has always handled correctly. The test skips cleanly if clang++
isn't installed or doesn't emit rnglistx for the source.

Reported by @MikeWang000000.

Fixes https://github.com/rui314/mold/issues/1578
2026-05-12 15:53:46 -07:00
Bennett Amodio 10b37a81fb Don't emit PT_INTERP for -shared outputs
When --dynamic-linker is passed alongside -shared, mold currently emits
a .interp section and a PT_INTERP program header. Both lld and BFD ld
silently drop --dynamic-linker for -shared outputs because shared
libraries are loaded by the dynamic linker rather than bootstrapped by
the kernel, so PT_INTERP serves no purpose there.

Gate .interp creation on (!shared || pie) so we keep it for regular
executables, -pie executables, and -shared -pie runnable shared
libraries (the libc.so.6-style case), and drop it only for the pure
shared-library case. The PT_INTERP program header at
output-chunks.cc:219 is conditional on ctx.interp being non-null, so
the segment vanishes automatically with no second edit.

Fixes https://github.com/rui314/mold/issues/1572
2026-05-08 16:28:51 -07:00
Rui Ueyama 45970e661d Update mold.1 (automated commit) 2026-05-08 08:06:27 +00:00
Rui Ueyama 155a8106c9 Merge pull request #1576 from bamo/feat/cli-no-warnings
Support -w / --no-warnings to suppress warnings
2026-05-08 17:05:35 +09:00
Bennett Amodio 51b9683ce7 Support -w / --no-warnings to suppress warnings
The Warn() machinery has consulted ctx.arg.suppress_warnings since
6d2dbd42 ("Preparation to support Apple ld64's -w option", March
2023), but no cmdline flag ever set it. Wire -w (the GNU/BFD
spelling) and --no-warnings (the lld long form) to set the field.

Because Warn::Warn early-returns when the flag is true, before the
ctx.arg.fatal_warnings branch sets has_error, -w also cancels
--fatal-warnings — matching lld's documented behavior.

The "warnings only" semantics match GCC, clang, and lld: real errors
still print and still fail the link. BFD ld's -w is broader and
suppresses error messages too, which would hide the cause of link
failures.

Fixes https://github.com/rui314/mold/issues/1493
2026-05-07 15:44:49 -07:00
Rui UeyamaandClaude Opus 4.7 e2d0c1a93b Accept ! as a bracket-expression negation marker in glob patterns
Linker scripts produced for lld (e.g., the version script Chrome's
build emits) use the POSIX/shell convention `[!...]` to negate a
bracket character class. mold previously accepted only `[^...]`, so
those scripts failed to match. Accept both, treating `!` and `^`
identically at the start of a bracket expression.

Fixes https://github.com/rui314/mold/issues/1575

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-07 12:18:41 +09:00
Rui UeyamaandClaude Opus 4.7 6b0ecea236 Don't drop DSO undef references when versym is VER_NDX_LOCAL
Commit 6b8293bf changed the early `ver` computation in
SharedFile::parse so that undef symbols use the raw versym instead
of being forced to VER_NDX_GLOBAL. Some BFD ld versions (notably
binutils 2.35 on Debian 11) emit VER_NDX_LOCAL (0) as the versym
for unversioned undefined dynamic symbols, so the subsequent
`if (ver == VER_NDX_LOCAL) continue;` check silently dropped those
undef references. With the DSO's reference invisible to mold, the
matching object-file definition was never marked as exported, and
at runtime the dynamic loader failed with "undefined symbol".

Restore the old behavior: defined symbols with VER_NDX_LOCAL are
still skipped (they aren't really exposed for dynamic linking),
but undef references are always processed.

Also fix the verdef/verneed merge in the same path. vd_ndx and
vna_other share a single versym index space (a producing linker
assigns them from one counter), so a single vector covers both.
read_verdef and read_verneed are folded into one read_version_strings
that populates that shared-index vector directly, replacing the
buggy `append` concatenation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-06 23:32:27 +09:00
Rui UeyamaandClaude Opus 4.7 6b8293bf8c Use .gnu.version_r when registering undefined DSO symbols
For an undefined symbol in a shared library, mold treated the versym
as VER_NDX_GLOBAL and registered the symbol under its bare name. That
meant an undef foo@VER_1.0 in libbar.so was looked up as just foo, so
a non-default foo@VER_1.0 in another DSO did not satisfy it and
--no-allow-shlib-undefined wrongly reported "undefined symbol: foo".

Parse .gnu.version_r and, when an undef has a versym indexing into it,
register the symbol as name@VERSION so the existing versioned-symbol
resolution path picks up matching definitions.

Fixes https://github.com/rui314/mold/issues/1571

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-06 18:29:32 +09:00
Rui UeyamaandClaude Opus 4.7 82b5ed48bf Fix flaky compress-debug-sections-{zlib,zstd}-level tests on Alpine
`readelf | grep -q` plus `set -o pipefail` is fragile: when grep -q
matches it stops reading immediately, readelf gets SIGPIPE on its next
write, and pipefail propagates the resulting 141. Alpine (musl) hits
this where glibc happens to escape it. Drop -q so grep drains stdin.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-06 15:57:23 +09:00
Rui UeyamaandClaude Opus 4.7 4d313a269c Rename gc-sections.cc visit() to avoid std::visit ADL clash on gcc 10
The new StartStopMap parameter contains std::string_view, so the
unqualified call to visit() inside mark() and the recursive call inside
visit() itself were resolved to std::visit by ADL on gcc 10 (Debian 11),
breaking the build.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-06 14:40:33 +09:00
Rui UeyamaandClaude Opus 4.7 384527fd29 Don't unconditionally retain C-identifier-named sections in --gc-sections
A section whose name is a valid C identifier was treated as a GC root,
keeping it (and everything reachable from it) alive even when nothing
referenced its synthesized __start_/__stop_ marker symbols. That could
pull in unrelated code with undefined references and cause spurious
"undefined symbol" errors that lld and ld.bfd don't produce.

Build a multimap of c-identifier-named sections up front and, during
marking, when a relocation targets __start_<name> or __stop_<name>,
mark every section named <name> alive.

Fixes https://github.com/rui314/mold/issues/1571

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-05 17:50:33 +09:00
Rui Ueyama d947b1a6d3 Update mold.1 (automated commit) 2026-05-04 14:00:41 +00:00
Rui UeyamaandClaude Opus 4.7 9e32fce011 Fix CI
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-04 22:53:57 +09:00
Rui UeyamaandClaude Opus 4.7 b7102d26ca Add hidden --use-android-relr-tags option
Pre-Android-12 (API 30 and earlier) bionic loaders predate the official
ELF gABI assignment of DT_RELR/DT_RELRSZ/DT_RELRENT and use legacy
OS-specific tag values DT_ANDROID_RELR (0x6fffe000), DT_ANDROID_RELRSZ
(0x6fffe001), DT_ANDROID_RELRENT (0x6fffe003), and section type
SHT_ANDROID_RELR (0x6fffff00). With --use-android-relr-tags, mold emits
those instead of the standard ones so binaries linked with
--pack-dyn-relocs=relr can be loaded on those older devices. The flag
is undocumented (not listed in --help) since it's only relevant to
shipping to legacy Android targets.

https://github.com/rui314/mold/issues/1544

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-04 22:53:56 +09:00
Rui UeyamaandClaude Opus 4.7 59957fce3b Add --pack-dyn-relocs=android support
Implements the Android Packed Relocations Format (APS2) used by Android
binaries to compress dynamic relocations. Two new modes are accepted:
`--pack-dyn-relocs=android` encodes all dynamic relocations as APS2 in
.rela.dyn (SHT_ANDROID_RELA) and emits DT_ANDROID_RELA(SZ) instead of
DT_RELA(SZ); `--pack-dyn-relocs=android+relr` is the same but with
R_RELATIVE relocations diverted to .relr.dyn via the existing RELR path.

The encoder groups consecutive relocations sharing offset_delta and
r_info to amortize SLEB128 overhead. The encoded bytes are decoded by
Android's bionic loader and by llvm-readelf, which the new test uses to
verify correctness.

main.cc gains a final set_osec_offsets() call after fix_synthetic_symbols.
Symbol values assigned there change addends for relocations that
reference synthetic symbols, which can shift the SLEB128-encoded section
size; re-running the layout convergence keeps sh_size consistent with
the final encoding.

Fixes https://github.com/rui314/mold/issues/1544

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-04 22:53:56 +09:00
Rui UeyamaandClaude Opus 4.7 c36c5a64fc Centralize dynamic relocation generation in RelDynSection
Previously each chunk wrote its dynamic relocations into a magic offset
in .rel.dyn (tracked via per-chunk reldyn_offset), with a separate
construct_relr pass building per-chunk relr vectors. Now each chunk
returns its dynrels via a virtual collect_dynrels(), and RelDynSection
aggregates them into a single relocs vector that it owns and writes.
RELR-eligibility is determined by checking r_offset alignment of the
collected ElfRel entries (replacing the ABS_REL_RELR kind).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-04 22:53:55 +09:00
Rui Ueyama e71cef7448 Update mold.1 (automated commit) 2026-04-27 06:01:09 +00:00
Rui UeyamaandClaude Opus 4.7 6695f13dce Add --compress-debug-sections=zlib:N for configurable zlib compression level
Generalize the zstd:N syntax added in the previous commit so that the
zlib compression level can also be specified. zlib levels 0 through 9
are accepted, where 0 means no compression. Plain `zlib` remains
equivalent to `zlib:1` (the previous hardcoded default).

Also drop the objcopy/zstdcat round-trip from the zstd-level test in
favor of a simpler readelf check, matching the new zlib-level test, and
add error-message coverage for out-of-range levels.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 15:00:26 +09:00
Rui Ueyama ab704cf476 Update mold.1 (automated commit) 2026-04-27 05:47:52 +00:00
Rui Ueyama c028765bb7 Merge pull request #1569 from pierluigilenoci/feat/zstd-compression-level
Add --compress-debug-sections=zstd:N for configurable zstd level
2026-04-27 14:39:37 +09:00
Pierluigi Lenoci 04209593c1 refactor: use zstd:<level> syntax for consistency with lld
Change the compression level syntax from zstd-<level> to zstd:<level>
as requested by @rui314 for consistency with lld.

Signed-off-by: Pierluigi Lenoci <[email protected]>
2026-04-26 20:23:28 +02:00
Pierluigi Lenoci 56fc57198a Add --compress-debug-sections=zstd-N for configurable zstd compression level
Allow users to specify a zstd compression level (1-22) for debug section
compression via --compress-debug-sections=zstd-N, where N is the desired
level. Higher levels achieve better compression ratios at the cost of
compression speed. The plain 'zstd' option remains equivalent to 'zstd-3'
(the previous hardcoded default).

Closes: https://github.com/rui314/mold/issues/1290
Signed-off-by: Pierluigi Lenoci <[email protected]>
2026-04-24 17:15:01 +02:00
Rui UeyamaandClaude Opus 4.7 84c411b9a2 [LoongArch] Don't relax GOT load to pcaddi when symbol is misaligned
pcaddi computes PC + (imm << 2), so the relaxation from a GOT load
(pcalau12i + ld.d) to pcaddi is valid only when the symbol's address
is 4-byte aligned. Without this check, a reference to a misaligned
symbol was relaxed to a pcaddi that resolves to the address with its
low two bits cleared, producing a wrong pointer.

Fixes https://github.com/rui314/mold/issues/1567

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-19 17:23:40 +09:00
Rui UeyamaandClaude Opus 4.7 6f8d62e21c Fix non-deterministic --gdb-index output with multiple threads
The parallel writer that fills (type, cu_idx) tuples for each name
appended entries in thread-scheduling order, so names referenced from
multiple compilation units produced different .gdb_index bytes across
runs. Sort each name's tuple array after the parallel append so the
output is reproducible while preserving parallelism.

Fixes https://github.com/rui314/mold/issues/1566

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-18 11:28:03 +09:00
Rui Ueyama 1b2306a71b Fix test failure on SPARC
We need to ignore SPARC-specific symbol with STT_REGISTER.

Fixes https://github.com/rui314/mold/issues/1564
2026-04-14 20:50:28 +09:00
Rui Ueyama a91381b1b1 Bump mold version to 2.41.0 2026-04-13 18:31:07 +09:00
Rui Ueyama 5b8c7dc023 Merge pull request #1547 from sicherha/fix_textrel2_test_on_Fedora_44
Fix `textrel2` test on Fedora 44
2026-04-07 16:46:48 +09:00
Rui Ueyama a1ae8da0c4 Fix --omagic to pack all sections into a single PT_LOAD segment
Previously, --omagic only flipped segment permissions to RWX but
still page-aligned PT_LOAD segments and emitted PT_GNU_RELRO, so
text and data ended up in separate page-padded segments. This
mismatched the documented behavior ("makes the first data segment
not aligned to a page boundary") and GNU ld's --omagic, which also
disables shared linking.

Make --omagic imply --Bstatic, disable RELRO, and stop forcing
PT_LOAD's p_align to the page size. Also gate chunk->is_relro on
z_relro so a disabled RELRO no longer leaks into address layout.

Fixes https://github.com/rui314/mold/issues/1562
2026-04-07 16:41:35 +09:00
Rui UeyamaandClaude Opus 4.6 632a0eead1 Fix spurious "duplicate symbol" errors with LTO
When linking with LTO, archive members extracted pre-LTO to satisfy
references from IR objects could cause false duplicate symbol errors.
This happened in two ways:

1. The pre-LTO check_duplicate_symbols flagged conflicts between LTO IR
   objects and regular objects. These conflicts are handled by the LTO
   backend and should not be reported.

2. After LTO compilation, archive members that were extracted pre-LTO
   retained stale is_reachable flags even when the LTO output superseded
   all their symbols. The post-LTO duplicate check then saw both the
   archive member and the LTO output defining the same symbol.

Fix both issues:
- In check_duplicate_symbols, skip errors where one side is an LTO IR
  object and the other is not. Only IR-vs-IR duplicates need catching.
- In do_lto, reset reachability for archive members before the post-LTO
  resolve_symbols so that only genuinely needed members are re-extracted.

Fixes https://github.com/rui314/mold/issues/1421

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-05 19:55:33 +09:00
Rui Ueyama 04430ade4b Merge pull request #1546 from jwakely/linker-script-group-as-needed
Fix getting output type from linker scripts that use AS_NEEDED
2026-04-05 14:35:21 +09:00
Rui UeyamaandClaude Opus 4.6 91a5f3c4bf Skip nightly build-all workflow when there are no new commits
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-05 14:28:23 +09:00
Rui Ueyama e804c8caff Fix a compiler warning 2026-04-05 13:47:07 +09:00
Rui Ueyama f378b18369 Support R_RISCV_GOT32_PCREL
This change should unbreak our riscv64 buildbot by fixing
test/relative-vtables.sh test.
2026-04-05 13:47:07 +09:00
Rui Ueyama ff83f60fbc Merge pull request #1557 from akarle/ak/fix-separate-debug-file-corruption
Fix separate-debug-file strtab corruption
2026-04-04 15:58:17 +09:00
Rui Ueyama f8b49ee83e Fix FreeBSD buildbot 2026-04-04 15:54:06 +09:00
Rui Ueyama 18ea70c696 Merge pull request #1554 from karesansui-u/fix/redo-main-abort-message
Replace abort() with diagnostic message in redo_main()
2026-04-04 15:48:29 +09:00
Rui Ueyama fd95adda95 "#undef ERROR" before use for MinGW compatibility
https://github.com/rui314/mold/pul/1558
2026-04-04 15:01:32 +09:00
Rui Ueyama 4e58828670 Simplify and add tests 2026-04-04 13:55:53 +09:00
Rui Ueyama 027111315e Merge pull request #1559 from shivendra02467/fix-shlib-undefined-visibility
[ELF] Fix --no-allow-shlib-undefined for hidden static providers and LTO
2026-04-04 13:45:12 +09:00
Rui Ueyama d4d5eae43d Do not report an error on LTO unclaimed files in archives
Previously, mold eagerly report an "not claimed by the LTO plugin" error
if it finds an LTO object file that is not claimed by an LTO plugin.
2026-04-04 11:34:42 +09:00
Rui Ueyama 274a46b9f7 Revert "defer incompatible archive LTO member errors until selected"
This reverts commit 6c4701955c.
2026-04-04 11:09:05 +09:00
Rui Ueyama 906af24d25 Merge pull request #1560 from tobim/push-rpuotyoounvk
Various LTO compatibility fixes
2026-04-04 11:07:20 +09:00
Rui UeyamaandClaude Opus 4.6 be28db1241 Support R_AARCH64_GOTPCREL32 relocation for relative vtables
This relocation is used by clang's -fexperimental-relative-c++-abi-vtables
to emit 32-bit GOT-relative PC-relative offsets in vtable entries (e.g. for
RTTI pointers). It resolves to G + GOT + A - P as a 32-bit value.

Fixes https://github.com/rui314/mold/issues/1561

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-03 19:46:19 +09:00
Tobias Mayer 6c4701955c defer incompatible archive LTO member errors until selected 2026-03-21 14:21:00 +01:00
Tobias Mayer ce0ee76550 Add regression test for mixed LLVM/GCC slim LTO archive link 2026-03-21 14:18:31 +01:00
Tobias Mayer 2a1e5c6e8d Fall back to native code for GCC fat LTO objects with LLVM plugin 2026-03-21 14:18:31 +01:00
Tobias Mayer 487b83e581 Add a test for mixed toolchain fatLTO
LTO links where the main driver is LLVM but some objects
are GCC fatLTO should succeed.
2026-03-21 14:18:31 +01:00
Tobias Mayer a660037f95 Preserve non-lto comdat members when section is shared with LTO objects 2026-03-21 10:24:17 +01:00
Tobias Mayer 10342f7c2a Add a regression test for mixed lto and assembly comdat 2026-03-21 10:02:05 +01:00
Shivendra Sharma f6bb1175eb [ELF] Fix --no-allow-shlib-undefined for hidden static providers and LTO
This patch fixes two distinct blind spots in how `mold` handles
--no-allow-shlib-undefined checks during the final link phase:

1. The Hidden Static Provider Bug (False Negative)
Previously, if a shared library requested a symbol that was provided by a
static object file with STV_HIDDEN, `mold` would silently overwrite the
global symbol visibility to STV_DEFAULT and rubber-stamp the binary,
resulting in a guaranteed `symbol lookup error` at runtime.
Fix: We now interrogate the raw `elf_syms` array of the static object
provider. If the original visibility is hidden/internal, we flag it as an
invalid provider and throw the error, matching GNU ld/lld behavior.

2. The LTO Shadowing Bug (False Positive)
When LTO is heavily utilized, GCC's plugin may optimize away symbols that
are theoretically provided by static objects if they aren't used internally.
If a shared library still needs that symbol, `mold`'s static state
registered the provider as NULL and threw an error, ignoring the fact
that a loaded DSO (like libc.so) offered a valid public fallback.
Fix: Introduced `has_public_dso_fallback()`. Before throwing an undefined
symbol error, `mold` performs a rapid scan of `ctx.dsos`. If a public
fallback exists dynamically, the error is safely suppressed.
2026-03-20 00:07:44 +05:30
Alex Karle 4cd4d5693e Fix separate-debug-file strtab corruption
Originally discussed in #1535, the debug data generated by
--separate-debug-file is being corrupted. In the best case scenario,
there's many blank/nameless undefined symbols in the symtab, but in
most production scenario I've encountered, the file is corrupt / gdb
refuses to load it:

    $ gdb main
    Reading symbols from $PWD/main.dbg...
    BFD: /$PWD/main.dbg: invalid string offset 4130582 >= 1345 for section `.strtab'
    BFD: /$PWD/main.dbg: invalid string offset 16777228 >= 1345 for section `.strtab'

    $ readelf -Ws main.dbg
    Symbol table '.symtab' contains 139 entries:
    [...]
    38: 0000000000000000     0 SECTION LOCAL  DEFAULT   38 .strtab
    39: 0000000000000000     0 SECTION LOCAL  DEFAULT   39 .symtab
    40: 0018a80200010000 0xb10000003f071500 NOTYPE  LOCAL  DEFAULT bad section index[3232] <corrupt>
    41: 0000003f07140000 0xfd02000100000cc2 NOTYPE  LOCAL  HIDDEN  bad section index[11500] <corrupt>
    42: 02000100000cd300 0x3f0712000008a3 <unknown>: 7 LOCAL  PROTECTED [<other>: 3c]   UND <corrupt>
    43: 3f07110000056202 0x100000cf5000000 NOTYPE  LOCAL  DEFAULT    1 <corrupt>

This appears to be happening because the strtab size is set to be larger
than its true value, causing garbage data to be placed in it, which
breaks the symtab.

This regression occurred in 5b71029, which fixed a different form of
corruption (the PHDR symbol escaping into the section headers, shifting
the section offsets), but created this strtab corruption. The root cause
is that `compute_section_headers` was moved prior to the wiping of
irrelevant chunks. `compute_section_headers` computes the strtab size by
adding up individual chunk strtab contributions. It needs to occur _after_
the wiping so that the strtab doesn't account for the data before the
wipe.

The core of this patch is really just a partial revert of 5b71029--the
compute_section_headers call appears to have been in the correct place
prior to this change; I've added a test that catches the issue by
intentionally bloating the size of the `.plt` section with many external
symbols. This section is wiped in the dbg data, so by bloating it, the
test can inspect for corruption.

Fixes #1535

Signed-off-by: Alex Karle <[email protected]>
2026-03-16 12:13:09 -04:00
karesansui 93e1aeaec6 Replace abort() with diagnostic message in redo_main()
When mold auto-detects an unsupported target from ELF headers,
redo_main() previously called abort() with no context. This replaces
it with a descriptive error message matching the style used by the
-m flag path in cmdline.cc.

Fixes #1553
2026-03-12 02:24:00 +09:00
Rui Ueyama fc96c1b9ef Do not call fallocate on tmpfs
Interestingly, calling fallocate on tmpfs is very slow. It took
almost a second for a fresh ~5 GiB file, even though intuititvely
it should finish very quickly.
2026-03-05 08:40:16 +09:00
Rui Ueyama 0c0c42b31d Add an entry for fedora-asahi-remix 2026-03-01 13:23:17 +09:00
Jonathan Wakely e0a6f8e1d3 Fix getting output type from linker scripts that use AS_NEEDED
Make Script<E>::get_script_output_type support AS_NEEDED inside an INPUT
or GROUP group, so that the libatomic_asneeded.so linker script used by
GCC 16 works.

Fixes #1545

Signed-off-by: Jonathan Wakely <[email protected]>
2026-02-28 09:08:49 +00:00
Rui Ueyama f8da34575f Unbreak Windows buildbot 2026-02-27 16:10:33 +09:00
Rui Ueyama a4db2382a5 Improve --gdb-index performance
This change eliminates the cost of zero-initialization on vector
resizing by using malloc/realloc instead. We also parallelize the
loops to write adddress areas and types to .gdb_index.
2026-02-27 15:45:51 +09:00
Rui Ueyama 6d6a128bf9 Handle -C and --chroot early
We read version script files in cmdline.cc, so we need to handle -C
and --chroot early. Otherwise, a version script passed by command line
can cause a "file not found" error.
2026-02-27 15:45:51 +09:00
Rui Ueyama aeb1041f1a Refactor 2026-02-27 15:45:51 +09:00
Christoph Erhardt 6e40a87aad Fix textrel2 test on Fedora 44
Tell the GNU linker to not report dynamic relocations in read-only
sections as an error.
2026-01-30 22:09:29 +01:00
Rui Ueyama 17956fdfa1 Merge pull request #1541 from mati865/push-qlyqwkunnpmx
Fix typo in test name
2025-12-12 09:43:18 +09:00
Rui Ueyama 97b43aaf0d Fix CI 2025-12-10 13:57:55 +09:00
Rui Ueyama c3bf8fd803 Attempt to fix CI 2025-12-10 12:59:41 +09:00
Rui Ueyama 4e51cfaa67 Update mold.1 (automated commit) 2025-12-10 03:29:09 +00:00
Rui Ueyama 87873cad57 Change the man page to reflect reality 2025-12-10 12:15:40 +09:00
Rui Ueyama 9f463a62ac Align the end of RELRO to a page boundary even if -z separate-loadable-segment is given
Otherwise the last page of a relro segment is not protected by the
runtime, as the runtime aligns down the end address of a RELRO segment.
2025-12-10 12:11:45 +09:00
Rui Ueyama 5b710298fc d1;10;0cAttempt to fix an issue with --separate-debug-file
https://github.com/rui314/mold/issues/1535
2025-11-17 16:01:14 +09:00
Rui Ueyama 4798f43358 Do not emit a bogus "PHDR" section for --separate-debug-file
Previously, we emit a bogus section with name "PHDR" for a separate
debug file.

Fixes https://github.com/rui314/mold/issues/1535
2025-11-15 12:42:53 +09:00
Rui Ueyama fcd5391981 Adapt to a recent readelf output format change
Fixes https://github.com/rui314/mold/issues/1533
2025-11-11 22:39:36 +09:00
Rui Ueyama ad08999d03 Refactor 2025-11-10 19:55:51 +09:00
Rui Ueyama 016bb8af6e Refactor 2025-11-05 20:13:14 +09:00
Rui Ueyama 3a5985ca14 Refactor 2025-11-04 22:06:18 +09:00
Rui Ueyama e99551c9f0 Refactor 2025-11-04 16:38:39 +09:00
Rui Ueyama 56f8eb3b92 Refactor 2025-11-04 15:54:44 +09:00
Rui Ueyama e459ed59c6 [SPARC64] Do not assume the dest reg of R_SPARC_GOTDATA_OP is always %g1
https://github.com/rui314/mold/issues/1529
2025-11-04 15:25:33 +09:00
Rui Ueyama 4d94410ad4 [SPARC64] Preserve %rd when rewriting R_SPARC_TLS_GD_ADD
https://github.com/rui314/mold/issues/1529
2025-11-04 15:21:58 +09:00
Rui Ueyama 069f4da96a [SPARC64] Change how we swap instructions for TLS_GD_CALL
https://github.com/rui314/mold/issues/1529
2025-11-04 15:21:52 +09:00
Rui Ueyama 01923e4b4b Refactor 2025-11-02 17:33:40 +09:00
Rui Ueyama 5cac744adf Merge pull request #1528 from koachan/main
[SPARC64] Preserve registers used during optimization transforms
2025-11-02 17:29:08 +09:00
Koakuma a88a2163b2 [SPARC64] Decouple rs1 and rd in R_SPARC_TLS_GD_LO10 transform
The compiler might place the two values on different registers,
so don't modify them during transforms.
2025-11-02 15:18:15 +07:00
Koakuma ca0b5973a2 [SPARC64] Preserve rd of TLS *_ADD relocations during optimization
GCC might decide to put the result of the operation in another register,
so remove hardcodings of %o0 as the destination register (`rd` field).
2025-11-02 00:40:20 +07:00
Rui Ueyama 5fb6d8aa63 Fix CI 2025-10-31 16:28:42 +09:00
Rui Ueyama 3ad34d4ec3 Do not create a PDE IFUNC entry if the ifunc is not referenced within the exe
Fixes https://github.com/rui314/mold/issues/1527
2025-10-31 15:47:06 +09:00
Rui Ueyama dce9ac1e5c Allow building mold for a specific set of targets
By popular request, this commit adds a new cmake variable that allows
building mold with a limited set of supported targets, which can help
reduce build time.

The use of this flag is discouraged unless you build mold frequently
for personal use. Otherwise, build mold with the default settings for
simplicity and maximum compatibility. In particular, distributions
should not build mold with a limited set of supported targets.

Fixes https://github.com/rui314/mold/issues/1364
2025-10-31 12:20:47 +09:00
Rui Ueyama 7cc59cc71a Fix a compiler warning 2025-10-30 15:39:21 +09:00
Rui Ueyama 98d7691c21 Refactor 2025-10-25 11:24:50 +09:00
Rui Ueyama 92adbb1d61 [SPARC64] Fix TLS GD with GCC -O2
Fixes https://github.com/rui314/mold/issues/1525
2025-10-25 11:13:21 +09:00
Rui Ueyama 8634570e56 Refactor 2025-10-23 21:05:50 +09:00
Rui Ueyama fce89c24e5 Respect --no-threads with --compress-debug-sections
Fixes https://github.com/rui314/mold/issues/1524
2025-10-23 12:28:39 +09:00
Rui Ueyama 2b36f8a982 Merge pull request #1523 from koachan/main
[SPARC64] Fix R_SPARC_TLS_GD_ADD handling
2025-10-23 10:04:45 +09:00
Rui Ueyama 7f8c50bfa7 [SPARC] Honor --no-apply-dnamic-relocs for R_SPARC_UA64
Related to: https://github.com/rui314/mold/pull/1523
2025-10-23 10:03:17 +09:00
Koakuma a16bb13a01 [SPARC64] Fix R_SPARC_TLS_GD_ADD handling
R_SPARC_TLS_GD_ADD can use any register for the base address (ELF Handling
For Thread-Local Storage, pg. 21), so remove the hardcoded %l7 usage.
2025-10-22 20:16:41 +07:00
Rui Ueyama f55ad918d4 [SPARC64] Emit dynamic relocations for R_SPARC_UA64
Related to: https://github.com/rui314/mold/pull/1523
2025-10-22 18:08:22 +09:00
Rui Ueyama 5164da8ad2 Fix CI 2025-10-09 17:20:06 +09:00
Rui Ueyama 7cc21c7657 Refactor 2025-10-09 16:30:37 +09:00
Rui Ueyama f8089ca121 Merge pull request #1522 from bmilanich/pr/eh_icf
Consider .gcc_except_table* sections for ICF.
2025-10-09 12:44:27 +09:00
Rui Ueyama 04a99003c2 Add missing newlines 2025-10-09 11:45:21 +09:00
Basil Milanich fd8659027b Consider .gcc_except_table* sections for ICF. 2025-10-08 14:12:07 -05:00
Rui Ueyama 0b20faa0cd [PPC32] Support R_PPC_DTPREL32 in non-alloc sections
Fixes https://github.com/rui314/mold/issues/1521
2025-10-03 10:47:27 +09:00
Rui Ueyama 7c82a6a00e Fix typo 2025-09-21 11:45:04 +09:00
Rui Ueyama 5148754c14 Refactor 2025-09-20 17:29:38 +09:00
Rui Ueyama 91439beea5 --section-order: Report an error if address goes backward
https://github.com/rui314/mold/issues/1514
2025-09-20 12:16:25 +09:00
Rui Ueyama 311da914d3 Refactor 2025-09-20 11:09:43 +09:00
Rui Ueyama dfb18afd69 Fix CI 2025-09-19 19:19:23 +09:00
Rui Ueyama 8dee52318b Run MemorySanitizer tests on GitHub Actions 2025-09-19 19:08:04 +09:00
Rui Ueyama 9d7f20b34e Avoid unnecessary zero-initializations 2025-09-19 19:08:04 +09:00
Rui Ueyama aa80487536 Merge pull request #1517 from etwoo/feature/msan-build
support MemorySanitizer w/ instrumented C++ stdlib
2025-09-19 15:16:32 +09:00
Eric Woo 13cec07aa3 fix msan error in compress.cc
==70801==WARNING: MemorySanitizer: use-of-uninitialized-value
0) 0x56218bab14b3 in mold::zlib_compress(std::__1::span<unsigned char, 18446744073709551615ul>) /home/ewoo/mold/lib/compress.cc:89:13
1) 0x56218bab14b3 in mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0::operator()(long) const /home/ewoo/mold/lib/compress.cc:106:17
2) 0x56218bab14b3 in decltype(std::declval<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&>()(std::declval<long&>())) std::__1::__invoke[abi:nn220000]<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /home/ewoo/llvm-project/build/include/c++/v1/__type_traits/invoke.h:251:25
3) 0x56218bab14b3 in std::__1::__invoke_result<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>::type std::__1::invoke[abi:nn220000]<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /home/ewoo/llvm-project/build/include/c++/v1/__functional/invoke.h:29:10
4) 0x56218bab14b3 in std::__1::__invoke_result<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>::type tbb::detail::d0::invoke<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /usr/include/tbb/../oneapi/tbb/detail/_utils.h:356:12
5) 0x56218bab14b3 in tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long>::operator()(tbb::detail::d1::blocked_range<long> const&) const /usr/include/tbb/../oneapi/tbb/parallel_for.h:206:13
6) 0x56218bab14b3 in decltype(std::declval<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&>()(std::declval<tbb::detail::d1::blocked_range<long>&>())) std::__1::__invoke[abi:nn220000]<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /home/ewoo/llvm-project/build/include/c++/v1/__type_traits/invoke.h:251:25
7) 0x56218bab14b3 in std::__1::__invoke_result<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>::type std::__1::invoke[abi:nn220000]<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /home/ewoo/llvm-project/build/include/c++/v1/__functional/invoke.h:29:10
8) 0x56218bab14b3 in std::__1::__invoke_result<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>::type tbb::detail::d0::invoke<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /usr/include/tbb/../oneapi/tbb/detail/_utils.h:356:12
9) 0x56218bab14b3 in tbb::detail::d1::start_for<tbb::detail::d1::blocked_range<long>, tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long>, tbb::detail::d1::auto_partitioner const>::run_body(tbb::detail::d1::blocked_range<long>&) /usr/include/tbb/../oneapi/tbb/parallel_for.h:117:9

Uninitialized value was created by an allocation of 'nbits.i.i.i.i.i.i.i.i.i' in the stack frame
0) 0x56218bab11e6 in mold::zlib_compress(std::__1::span<unsigned char, 18446744073709551615ul>) /home/ewoo/mold/lib/compress.cc:87:3
1) 0x56218bab11e6 in mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0::operator()(long) const /home/ewoo/mold/lib/compress.cc:106:17
2) 0x56218bab11e6 in decltype(std::declval<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&>()(std::declval<long&>())) std::__1::__invoke[abi:nn220000]<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /home/ewoo/llvm-project/build/include/c++/v1/__type_traits/invoke.h:251:25
3) 0x56218bab11e6 in std::__1::__invoke_result<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>::type std::__1::invoke[abi:nn220000]<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /home/ewoo/llvm-project/build/include/c++/v1/__functional/invoke.h:29:10
4) 0x56218bab11e6 in std::__1::__invoke_result<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>::type tbb::detail::d0::invoke<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&>(mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0 const&, long&) /usr/include/tbb/../oneapi/tbb/detail/_utils.h:356:12
5) 0x56218bab11e6 in tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long>::operator()(tbb::detail::d1::blocked_range<long> const&) const /usr/include/tbb/../oneapi/tbb/parallel_for.h:206:13
6) 0x56218bab11e6 in decltype(std::declval<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&>()(std::declval<tbb::detail::d1::blocked_range<long>&>())) std::__1::__invoke[abi:nn220000]<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /home/ewoo/llvm-project/build/include/c++/v1/__type_traits/invoke.h:251:25
7) 0x56218bab11e6 in std::__1::__invoke_result<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>::type std::__1::invoke[abi:nn220000]<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /home/ewoo/llvm-project/build/include/c++/v1/__functional/invoke.h:29:10
8) 0x56218bab11e6 in std::__1::__invoke_result<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>::type tbb::detail::d0::invoke<tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&>(tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long> const&, tbb::detail::d1::blocked_range<long>&) /usr/include/tbb/../oneapi/tbb/detail/_utils.h:356:12
9) 0x56218bab11e6 in tbb::detail::d1::start_for<tbb::detail::d1::blocked_range<long>, tbb::detail::d1::parallel_for_body_wrapper<mold::ZlibCompressor::ZlibCompressor(unsigned char*, long)::$_0, long>, tbb::detail::d1::auto_partitioner const>::run_body(tbb::detail::d1::blocked_range<long>&) /usr/include/tbb/../oneapi/tbb/parallel_for.h:117:9

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/ewoo/mold/lib/compress.cc:89:13 in mold::zlib_compress(std::__1::span<unsigned char, 18446744073709551615ul>)
2025-09-18 04:05:30 +00:00
Eric Woo 921649fc7d fix msan error in passes.cc
Uninitialized bytes in fwrite at offset 880 inside [0x724000002000, 7968)
==60359==WARNING: MemorySanitizer: use-of-uninitialized-value
0) 0x561279ddfd59 in mold::MallocOutputFile<mold::X86_64>::close(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/mold.h:1587:5

Uninitialized value was stored to memory at
0) 0x56127521426e in __msan_memcpy.part.0 msan_interceptors.cpp.o
1) 0x56127701a499 in void mold::write_vector<unsigned char>(void*, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>> const&) /home/ewoo/mold/src/../lib/lib.h:246:5
2) 0x56127701a499 in mold::BuildIdSection<mold::X86_64>::copy_buf(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/output-chunks.cc:2745:3

Uninitialized value was stored to memory at
0) 0x56127521412e in __msan_memmove.part.0 msan_interceptors.cpp.o
1) 0x561277df5cca in unsigned char* std::__1::__constexpr_memmove[abi:nn220000]<unsigned char, unsigned char>(unsigned char*, unsigned char*, std::__1::__element_count) /home/ewoo/llvm-project/build/include/c++/v1/__string/constexpr_c_functions.h:228:5
2) 0x561277df5cca in std::__1::pair<unsigned char*, unsigned char*> std::__1::__copy_trivial_impl[abi:nn220000]<unsigned char, unsigned char>(unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__algorithm/copy_move_common.h:64:3
3) 0x561277df5cca in std::__1::pair<unsigned char*, unsigned char*> std::__1::__copy_impl::operator()[abi:nn220000]<unsigned char, unsigned char, 0>(unsigned char*, unsigned char*, unsigned char*) const /home/ewoo/llvm-project/build/include/c++/v1/__algorithm/copy.h:234:12
4) 0x561277df5cca in std::__1::pair<unsigned char*, unsigned char*> std::__1::__copy_move_unwrap_iters[abi:nn220000]<std::__1::__copy_impl, unsigned char*, unsigned char*, unsigned char*, 0>(unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__algorithm/copy_move_common.h:94:19
5) 0x561277df5cca in std::__1::pair<unsigned char*, unsigned char*> std::__1::__copy[abi:nn220000]<unsigned char*, unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__algorithm/copy.h:241:10
6) 0x561277df5cca in unsigned char* std::__1::copy[abi:nn220000]<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__algorithm/copy.h:247:10
7) 0x561277df5cca in unsigned char* std::__1::__uninitialized_allocator_copy_impl[abi:nn220000]<std::__1::allocator<unsigned char>, unsigned char, unsigned char, 0>(std::__1::allocator<unsigned char>&, unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__memory/uninitialized_algorithms.h:574:12
8) 0x561277df5cca in unsigned char* std::__1::__uninitialized_allocator_copy[abi:nn220000]<std::__1::allocator<unsigned char>, unsigned char*, unsigned char*, unsigned char*>(std::__1::allocator<unsigned char>&, unsigned char*, unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__memory/uninitialized_algorithms.h:582:28
9) 0x561277df5cca in void std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>::__construct_at_end<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned long) /home/ewoo/llvm-project/build/include/c++/v1/__vector/vector.h:954:17
10) 0x561277df5cca in void std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>::__init_with_size[abi:nn220000]<unsigned char*, unsigned char*>(unsigned char*, unsigned char*, unsigned long) /home/ewoo/llvm-project/build/include/c++/v1/__vector/vector.h:602:7
11) 0x561277df5cca in std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>::vector[abi:nn220000]<unsigned char*, 0>(unsigned char*, unsigned char*) /home/ewoo/llvm-project/build/include/c++/v1/__vector/vector.h:214:5
12) 0x561277df5cca in void mold::write_build_id<mold::X86_64>(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/passes.cc:3270:29

Uninitialized value was created by an allocation of 'buf' in the stack frame
0) 0x561277df597e in void mold::write_build_id<mold::X86_64>(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/passes.cc:3266:5

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/ewoo/mold/src/mold.h:1587:5 in mold::MallocOutputFile<mold::X86_64>::close(mold::Context<mold::X86_64>&)
2025-09-18 04:04:25 +00:00
Eric Woo 33545031d7 fix msan error in input-sections.cc
==45321==WARNING: MemorySanitizer: use-of-uninitialized-value
0) 0x56205a24a8f8 in char const* std::__1::__constexpr_memchr[abi:nn220000]<char const, char>(char const*, char, unsigned long) /home/ewoo/llvm-project/build/include/c++/v1/__string/constexpr_c_functions.h:149:30
1) 0x56205a24a8f8 in std::__1::char_traits<char>::find[abi:nn220000](char const*, unsigned long, char const&) /home/ewoo/llvm-project/build/include/c++/v1/__string/char_traits.h:135:12
2) 0x56205a24a8f8 in unsigned long std::__1::__str_find[abi:nn220000]<char, unsigned long, std::__1::char_traits<char>, 18446744073709551615ul>(char const*, unsigned long, char, unsigned long) /home/ewoo/llvm-project/build/include/c++/v1/__string/char_traits.h:353:23
3) 0x56205a24a8f8 in std::__1::basic_string_view<char, std::__1::char_traits<char>>::find[abi:nn220000](char, unsigned long) const /home/ewoo/llvm-project/build/include/c++/v1/string_view:509:12
4) 0x56205a24a8f8 in mold::find_null(std::__1::basic_string_view<char, std::__1::char_traits<char>>, long, long) /home/ewoo/mold/src/input-sections.cc:363:17
5) 0x56205a24a8f8 in mold::MergeableSection<mold::X86_64>::split_contents(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/input-sections.cc:401:20

Uninitialized value was created by a heap allocation
0) 0x562058af198b in operator new(unsigned long) (/home/ewoo/mold/build/mold+0x23a98b) (BuildId: 29d9e90237b4b68ceff9639a3d9599d70f774d75)
1) 0x56205a2430fa in mold::InputSection<mold::X86_64>::uncompress(mold::Context<mold::X86_64>&) /home/ewoo/mold/src/input-sections.cc:65:13

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/ewoo/mold/src/input-sections.cc:363:17 in mold::find_null(std::__1::basic_string_view<char, std::__1::char_traits<char>>, long, long)
2025-09-18 04:03:09 +00:00
Eric Woo f800f5226c support MemorySanitizer w/ instrumented C++ stdlib 2025-09-18 04:02:39 +00:00
Rui Ueyama 8cf0b59e8f Update --help message 2025-09-15 08:04:28 +09:00
Rui Ueyama 729ceabd8c Update mold.1 (automated commit) 2025-09-14 14:34:10 +00:00
Rui Ueyama 131b5a9e89 Update man page 2025-09-14 23:32:00 +09:00
Rui Ueyama 056286d741 Partially revert 7dd7db0559
so that the output from --print-gc-sections and --print-icc-sections
remains the same as they were.

https://github.com/rui314/mold/pull/1507
2025-09-14 23:12:42 +09:00
Rui Ueyama 79ad0d2b4a Refactor 2025-09-14 19:13:35 +09:00
Rui Ueyama 6454b6c85a Merge pull request #1507 from pedronavf/print_sections_to_file
--print-gc-sections and --print-icf-sections can now take =FILE to s…
2025-09-14 19:11:30 +09:00
Rui Ueyama 49f9c5964a Do not convert read-only data to bss with --zero-to-data
Read-only data sections containing uninitialized variables are essentially
useless because reading from them always returns 0 at any time, so if they
exist, they are there for a reason. We do not want to automatically
convert it to a BSS.

https://github.com/rui314/mold/issues/1514
2025-09-14 10:36:03 +09:00
Rui Ueyama 3fb153e2ee Fix CI 2025-09-12 18:13:05 +09:00
Rui Ueyama 847341d208 Add --zero-to-bss
https://github.com/rui314/mold/pull/1515
2025-09-12 15:26:31 +09:00
Rui Ueyama 34c39e39f0 Refactor 2025-08-30 10:18:46 +09:00
Rui Ueyama 05827319da Fix test for system that doesn't support -static 2025-08-27 09:11:47 +09:00
Rui Ueyama ffbcfdb880 Merge pull request #1508 from PowellNGL/patch-1
Fix GCC package name for RHEL-based distros in install-build-deps.sh
2025-08-27 09:11:32 +09:00
Nick Powell 415176f530 Use correct GCC C++ package name for RHEL-based distros 2025-08-26 19:04:19 +02:00
Pedro Navarro 7dd7db0559 --print-gc-sections and --print-icf-sections can now take =FILE to save their output to a file 2025-08-26 02:26:41 -07:00
Rui Ueyama f5658e8bb4 Refactor 2025-08-26 11:24:12 +09:00
Rui Ueyama 8825ed7004 Merge pull request #1506 from minhbq-99/fix-pop-state
Correctly track --static value inside --push-state/--pop-state
2025-08-26 10:54:45 +09:00
Bui Quang Minh 001f718647 Correctly track --static value inside --push-state/--pop-state
Currently, when using --static inside --push-state and --pop-state, we
incorrectly mark the global state as static. This commit changes to the
correct behavior where global state is marked as static only when
--static is used at the outer most (outside of any --push-state,
--pop-state).
2025-08-25 23:02:40 +08:00
Rui Ueyama 4aa1a32d72 Bump required clang version from 12 to 16
Closes https://github.com/rui314/mold/issues/1505
2025-08-21 11:05:15 +09:00
Rui Ueyama 083901f42d Bump mold version to 2.40.4 2025-08-17 10:32:35 +09:00
Rui Ueyama 0a36282062 Revert "Choose the last file if we have more than one -l for the same DSO"
This reverts commit 2783e82436.

I don't want to take too much risk for 2.40.4, so I'll revert this change
for now.

https://github.com/rui314/mold/issues/1494
2025-08-16 12:21:41 +09:00
Rui Ueyama 754949f118 Revert "Upgrade bundled mimalloc to 3.0.8"
This reverts commit 1b76a11af2.

Closes https://github.com/rui314/mold/issues/1502
2025-08-15 11:48:32 +09:00
Rui Ueyama 05f5cdcf29 Add Alt Linux 2025-08-15 07:29:03 +09:00
Rui Ueyama 0ef640c95d Add Oracle Linux
Fixes https://github.com/rui314/mold/issues/1501
2025-08-15 06:58:13 +09:00
Rui Ueyama 7e0b728378 [ppc64v2] Support R_PPC64_GOT16
Fixes https://github.com/rui314/mold/issues/1497
2025-08-11 11:32:55 +09:00
Rui Ueyama d9b20a1580 [PPC64v2] Support R_PPC64_REL14
Fixes https://github.com/rui314/mold/issues/1496
2025-08-09 22:48:53 +09:00
Rui Ueyama 2783e82436 Choose the last file if we have more than one -l for the same DSO
Fixes https://github.com/rui314/mold/issues/1494

Based on https://github.com/rui314/mold/issues/1494
2025-08-04 20:33:44 +09:00
Rui Ueyama 9f5b7064dc Handle .data.rel.ro.{hot,unlikely} RELRO sections 2025-07-31 10:44:13 +09:00
Rui Ueyama f83747d6f6 Change the filename of Windows release binary 2025-07-31 06:33:29 +09:00
Rui Ueyama 282ca44e90 Bump mold version to 2.40.3 2025-07-30 13:42:59 +09:00
Rui Ueyama 201bc71b42 Revert f0b933820d
Emitting multiple zlib streams in a single .debug_info section broke
a few tools.

Fixes https://github.com/rui314/mold/issues/1491
2025-07-29 14:57:39 +09:00
Rui Ueyama de3a25f3ca [sh4] Use invalid instruction to fill gaps in executable sections
Quote from SH-4 CPU Core Architecture manual:

  The opcode 0xFFFD is guaranteed to be defined in any SH-4
  architecture revision. Other unused opcodes may be treated as
  reserved in any particular SH-4 implementation.

  The only undefined opcode which the architecture guarantees to cause
  a General Illegal Instruction Exception is 0xFFFD.
2025-07-28 10:06:57 +09:00
Rui Ueyama 5aa8809932 Refactor 2025-07-28 10:06:57 +09:00
Rui Ueyama ce04287237 [s390x] Fill gaps in executable sections with invalid insns instead of NOPs 2025-07-28 10:06:57 +09:00
Rui Ueyama 5485fdec1b [PPC64LE] Support R_PPC64_GOT16* relocations
Fixes https://github.com/rui314/mold/issues/1490
2025-07-27 18:33:25 +09:00
Rui Ueyama 69fee4966f Make --sysroot work with --chroot 2025-07-24 11:20:29 +09:00
Mateusz Mikuła afca89faaa Fix typo in test name 2025-07-23 22:07:16 +02:00
Rui Ueyama 1b76a11af2 Upgrade bundled mimalloc to 3.0.8 2025-07-22 14:40:02 +09:00
Rui Ueyama a531ce43b7 Build daily Windows archives 2025-07-19 16:41:13 +09:00
Rui Ueyama 6fb9564b4a Build a Zip file for Windows binary distribution 2025-07-18 09:44:16 +09:00
Rui Ueyama 4e44823cc0 Refactor 2025-07-15 15:33:51 +09:00
Rui Ueyama dd44e4768d Add a comment 2025-07-15 15:29:43 +09:00
Rui Ueyama 00efcf3d36 Refactor 2025-07-15 11:55:52 +09:00
Rui Ueyama 01358f680d Remove an issue template 2025-07-15 11:43:18 +09:00
Rui Ueyama 6e8852ea2a Allow partially-quoted tokens in response files
Fixes https://github.com/rui314/mold/issues/1485
2025-07-15 10:04:16 +09:00
Rui Ueyama 12e701cf32 Fix test
Closes https://github.com/rui314/mold/issues/1483
2025-07-14 21:44:44 +09:00
Rui Ueyama 5a144e161e Merge pull request #1482 from sicherha/fix_mimalloc_on_raspberry_pi
Build bundled mimalloc library without architecture-specific optimisations
2025-07-14 14:19:52 +09:00
Christoph Erhardt 278750593f Build bundled mimalloc library without architecture-specific optimisations
On 64-bit ARM, mimalloc is built with the compiler flag
`-march=armv8.1-a` by default. This flag advises GCC to make use of
ARMv8.1-A-specific instructions such as `ldaddal`. Machines based on the
older ARMv8-A instruction set - for example the Raspberry Pi 4 -
experience 'Illegal instruction (core dumped)' crashes as a result.

To ensure compatibility with machines that do not support ARMv8.1-A,
disable architecture-specific optimisations by setting the CMake option
`MI_NO_OPT_ARCH` to `ON`.

As of mimalloc 2.2.3, 64-bit ARM is the only architecture affected by
the `MI_NO_OPT_ARCH` option.
2025-07-13 18:26:57 +02:00
Rui Ueyama b862729608 Merge pull request #1481 from Ni-g-3l/add-zorin-distrib
Add Zorin Distribution to install deps script
2025-07-12 15:30:22 +09:00
Nig3l 6a842c7182 Add Zorin Distribution to install deps script 2025-07-12 07:14:58 +02:00
Rui Ueyama e61093dfd6 Bump mold version to 2.40.2 2025-07-12 10:36:11 +09:00
Rui Ueyama a01599cb4d Refactor 2025-07-08 09:38:53 +09:00
Rui Ueyama 87e4c7f552 Update toolchains 2025-07-02 13:10:41 +09:00
Rui Ueyama fa74740703 Refactor 2025-06-30 16:10:42 +09:00
Rui Ueyama 121c0ed23d Accept DSO-only linking
Fixes https://github.com/rui314/mold/issues/1478
2025-06-29 09:34:44 +09:00
Rui Ueyama ed7ccccf54 Add comments 2025-06-27 21:54:48 +09:00
Rui Ueyama 23789a7936 Add an overflow check for R_LARCH_32_PCREL 2025-06-27 09:58:38 +09:00
Rui Ueyama df91548a62 Add comments 2025-06-26 22:39:46 +09:00
Rui Ueyama bc7e4af733 Refactor 2025-06-26 10:25:40 +09:00
Rui Ueyama 2f6cf922e9 Add comments 2025-06-25 14:17:14 +09:00
Rui Ueyama 949e72307f Check certificates when downloading source archives
Pointed out at https://github.com/rui314/mold/discussions/1477
2025-06-25 10:35:29 +09:00
Rui Ueyama b3778207fc Refactor 2025-06-24 14:18:24 +09:00
Rui Ueyama 8eafdfbd0a Add a timer 2025-06-24 13:18:47 +09:00
Rui Ueyama 72c0340ca5 Ignore -fuse-ld
GCC passes the flag to the linker for some reason.
2025-06-24 13:07:49 +09:00
Rui Ueyama 934ca5bb5b Refactor 2025-06-24 10:54:05 +09:00
Rui Ueyama c9fa7111a2 Ensure that our Integer types are compiled to single load/store instructions
It turns out that at least clang-18 fails to compile our code to load
a 32-bit native-endian value from memory using shift + bitwise OR into
a single instruction when compiled with `-march=native`. It instead
compiles the code to complicated vector instructions.

It seems that the good old bswap and memcpy are the best option to
prevent the compiler from overthinking.
2025-06-23 15:09:50 +09:00
Rui Ueyama a3682ddbfd Refactor 2025-06-23 11:20:23 +09:00
Rui Ueyama 75772401cc Make loongarch64 build reproducible 2025-06-20 14:06:24 +09:00
Rui Ueyama 36f8ead511 Refactor 2025-06-20 14:06:24 +09:00
Rui Ueyama 30303d716a Fix test 2025-06-19 10:19:46 +09:00
Rui Ueyama 281d78caea Add a test 2025-06-19 09:53:58 +09:00
Rui Ueyama e9afa21adc Merge pull request #1475 from andreas-schwab/main
[RISC-V] Fix range checks for HI20/LO12 reloc addends
2025-06-19 09:18:15 +09:00
Andreas Schwab 52c698d625 [RISC-V] Fix range checks for HI20/LO12 reloc addends
Fixes #1474

Signed-off-by: Andreas Schwab <[email protected]>
2025-06-18 15:46:46 +02:00
Rui Ueyama 7eedb949b7 [RISC-V] Add additional relocation overflow checks
Fixes https://github.com/rui314/mold/issues/1474
2025-06-18 21:00:19 +09:00
Rui Ueyama 85408b4976 Refactor 2025-06-18 16:22:58 +09:00
Rui Ueyama 4cb46531d0 Refactor 2025-06-17 15:31:17 +09:00
Rui Ueyama ea98b0cb71 Refactor 2025-06-17 11:13:50 +09:00
Rui Ueyama 27edd50815 Fix CI 2025-06-16 14:11:09 +09:00
Rui Ueyama 18ecf979b8 Make the size of the thunk entry vary based on the distance to the target 2025-06-16 14:02:58 +09:00
Rui Ueyama 47f6c2839c Long range extension thunk for ARM64 2025-06-14 10:22:29 +09:00
Rui Ueyama da144e9d23 Refactor 2025-06-12 14:34:46 +09:00
Rui Ueyama f556d2b4ae Extend CI timeout 2025-06-11 22:02:41 +09:00
Rui Ueyama f2a62f70ef Set timeout to CI 2025-06-11 21:13:20 +09:00
Rui Ueyama 1956ae6ac8 Close fd when doing LTO
Fixes https://github.com/rui314/mold/issues/1362
2025-06-11 10:21:59 +09:00
Rui Ueyama 96c798d7be Bump mold version to 2.40.1 2025-06-08 10:06:35 +09:00
Rui Ueyama 6214f7be34 Refactor 2025-06-07 14:06:13 +09:00
Rui Ueyama 9b8b666da8 Update mold.1 (automated commit) 2025-06-07 04:03:43 +00:00
Rui Ueyama 6465232f0c Update a man page
Fixes https://github.com/rui314/mold/issues/1471
2025-06-07 13:02:20 +09:00
Rui Ueyama cc32a6d559 Reduce memory usage of Aho-Corasick search 2025-06-07 12:43:54 +09:00
Rui Ueyama 6214e132f8 Add a comment 2025-06-06 15:03:25 +09:00
Rui Ueyama 6ca242b8f0 Refactor 2025-06-06 14:24:13 +09:00
Rui Ueyama 3721c52d34 Refactor 2025-06-06 10:11:28 +09:00
Rui Ueyama 341c88e426 Refactor 2025-06-05 23:01:55 +09:00
Rui Ueyama 8f3f71663e Refactor 2025-06-05 18:54:39 +09:00
Rui Ueyama 1012a4804c Refactor 2025-06-05 18:13:47 +09:00
Rui Ueyama 5962014fd8 Fix CI 2025-06-05 17:40:58 +09:00
Rui Ueyama dac20fa243 Improve multi-glob pattern matcher so that it's linear-time
I asked ChatGPT how to match multiple glob patterns simultaneously with a
given input string, while avoiding the textbook-style NFA-to-DFA conversion.
Then it suggested that I implement a bitvector-based NFA simulation
algorithm that I wasn't aware. I don't think I could have come up with it
myself easily. This is impressive. ChatGPT is so good at programming and
sometimes much better than me! It may not be oto long before AI writes all
the code for me.
2025-06-05 17:14:34 +09:00
Rui Ueyama 8fa6fc86d3 Optimize glob matcher 2025-06-05 10:32:09 +09:00
Rui Ueyama 1c64d40ab8 Do not use recursion for glob pattern matching
Our glob pattern matcher was an exponential-time implementation.
This can easily be improved by avoiding recursion as explained at
https://research.swtch.com/glob.
2025-06-05 09:12:57 +09:00
Rui Ueyama 58cf042386 Refactor 2025-06-04 14:46:21 +09:00
Rui Ueyama d2f8bffdba Rename lib/common.h -> lib/lib.h 2025-06-04 14:23:43 +09:00
Rui Ueyama 60a9e013d3 Move jobs-{unix,win32}.cc from lib/ to src/ 2025-06-04 13:30:40 +09:00
Rui Ueyama a8893494ae Revert "Use std::syncostream"
This reverts commit 2479b81f8a because
it cannot be built with clang-16.
2025-06-04 12:49:52 +09:00
Rui Ueyama 2479b81f8a Use std::syncostream 2025-06-04 10:45:04 +09:00
Rui Ueyama 4dd133a31e Move {mapped-file,signal}-{unix,win32}.cc from lib/ to src/
The distinction between lib and src no longer makes much sense since
we no longer support Mach-O.
2025-06-04 10:44:54 +09:00
Rui Ueyama 02678c8af0 Refactor 2025-06-03 19:41:31 +09:00
Rui Ueyama 29e8b83886 Refactor 2025-06-03 16:56:23 +09:00
Rui Ueyama f0b933820d Emit multiple zlib streams to compressed sections
I learned that tools supporting compressed sections generally support
concatenated zlib streams within a single compressed debug section.
Examples include GNU/LLVM binutils and libbacktrace. So let's emit
compressed data that way, because it's much easier to do.

It is actually desirable to emit multiple small compressed blocks for
extremely large debug sections, because zlib doesn't support compressed
streams larger than 4 GiB on machines where the long type is 32 bits.

Context: https://sourceware.org/pipermail/binutils/2025-June/141514.html
2025-06-03 13:53:13 +09:00
Rui Ueyama 9a2f31164b Refactor 2025-06-03 08:44:29 +09:00
Rui Ueyama 8ef93b14d6 Refactor 2025-06-02 12:03:19 +09:00
Rui Ueyama 8d57a4419d Refactor 2025-06-02 11:35:21 +09:00
Rui Ueyama 0ef77ebd98 Do not use std::vector<u8> to avoid zero-initialization 2025-06-02 09:31:15 +09:00
Rui Ueyama 00d45d317f Merge pull request #1470 from a5ehren/cachyos 2025-06-02 06:57:32 +09:00
Ehren Bendler cdb63bb2a1 Update install-build-deps.sh to know what cachyos is
It's just a popular arch fork, so put it in the arch block
2025-06-01 11:52:56 -04:00
Rui Ueyama bcd77395d4 Use & instead of \0 in sed
\0 is not a portable way to represent the entire match; & is.
2025-06-01 10:52:55 +09:00
Rui Ueyama 4074707891 Fix CI 2025-06-01 10:52:55 +09:00
Rui Ueyama 079f914898 Use all available cores to compress debug sections 2025-06-01 10:03:05 +09:00
Rui Ueyama d59c55959c Avoid zero-initializing a very large vector
Chunk<E>::uncompressed_data can be very large as it contains an entire
output section contents. std::vector<u8>::resize() zero-initializes the
contents. We want to avoid that cost.

On my AMD Threadripper 7980X machine, zero-initializing 1 GiB of
memory takes ~200 ms. I can confirm that this change shaves off ~1.2s
for an executable with 5 GiB of debug info sections in uncompressed
form.
2025-06-01 10:03:05 +09:00
Rui Ueyama 5e19601a17 Fix debug section prefix 2025-05-31 11:32:50 +09:00
Rui Ueyama 3de3dcc001 Remove unused #include 2025-05-31 09:01:12 +09:00
Rui Ueyama 0d8334e5a2 Report an error if .dynsym refers to a section whose index is >65280
Unlike .symtab, .dynsym cannot have an extended symbol table, so a
section index that is larger than 65279 is not representable in an
output.

Fixes https://github.com/rui314/mold/issues/1468
2025-05-30 11:48:19 +09:00
Rui Ueyama a731b4c58c Build distribution binaries using GitHub Actions 2025-05-29 21:01:43 +09:00
Rui Ueyama 971317f3a9 Fix CI 2025-05-29 15:00:25 +09:00
Rui Ueyama 0a55058042 Do not link with the bundled TBB if BLAKE3_USE_TBB is ON
Recent versions of BLAKE3 library can be built with OneTBB for parallelization.
If that is enabled, we don't want to build and link our bundled OneTBB to avoid
conflict.

Fixes https://github.com/rui314/mold/issues/1466
2025-05-29 11:52:28 +09:00
Rui Ueyama 32746ebbb3 Upgrade bundled blake3 to v1.8.2 2025-05-29 11:51:01 +09:00
Rui Ueyama 1f44438b63 Add Manjaro Linux 2025-05-29 11:35:10 +09:00
Rui Ueyama b53197300b Bump mold version to 2.40.0 2025-05-26 11:30:00 +09:00
Rui Ueyama 3e9321b9a0 Fix test flakiness 2025-05-26 11:30:00 +09:00
Rui Ueyama 142eeaba86 Fix CI 2025-05-25 11:42:00 +09:00
Rui Ueyama 15f6b9f966 Refactor 2025-05-24 13:43:17 +09:00
Rui Ueyama a310cd27d4 Refactor 2025-05-23 18:33:04 +09:00
Rui Ueyama 2d178eb1ed Fix ASAN failure on an invalid downcast 2025-05-22 20:35:04 +09:00
Rui Ueyama 0fdffad57f Fix an assertion failure when a word-size weak abs reloc is promoted to dynamic
Fixes https://github.com/rui314/mold/issues/1462
2025-05-22 19:53:19 +09:00
Rui Ueyama d05272c4ee Fix CI 2025-05-22 12:11:22 +09:00
Rui Ueyama 1683826f2f Fix tests
Closes https://github.com/rui314/mold/issues/1461
2025-05-22 10:34:29 +09:00
Rui Ueyama 9ddec785b1 Do not sort debug info sections unless we have a mix of DWARF32 and DWARF64 2025-05-21 21:38:55 +09:00
Rui Ueyama e4f867ad77 Stabilize test 2025-05-21 21:38:55 +09:00
Rui Ueyama b7ce902e52 Add a test 2025-05-20 22:23:10 +09:00
Rui Ueyama 133613a493 Refactor 2025-05-20 21:29:37 +09:00
Rui Ueyama b026d7119b Sort debug sections in a separate debug file 2025-05-20 18:27:56 +09:00
Rui Ueyama fc0dd85d87 Add a comment 2025-05-20 18:01:27 +09:00
Rui Ueyama 6577232458 Rename a variable 2025-05-20 16:22:19 +09:00
Rui Ueyama b504cacf25 Fix CI 2025-05-20 15:45:20 +09:00
Rui Ueyama 159ce3ba67 Place strings referenced by DWARF32 at the beginning of .debug_str 2025-05-20 15:29:50 +09:00
Rui Ueyama 4c2dae0a2d Fix CI 2025-05-20 14:11:42 +09:00
Rui Ueyama 275c53d615 Merge pull request #1459 from el-remph/static-ldso
Fix linked executable segfaulting if -I and -static are both passed
2025-05-20 11:43:36 +09:00
Rui Ueyama fdd959880a Sort not only .debug_info but also other debug info sections
I believe it is unlikely that sections such as .debug_line grow beyond
4 GiB to cause relocation overflows, but doing some extra work is
trivial, so just do it.
2025-05-20 11:35:07 +09:00
Rui Ueyama 26d827d6e7 Read not only one CU but all CUs in .debug_info 2025-05-19 23:37:58 +09:00
Rui Ueyama 858fd8c495 Refactor 2025-05-19 23:35:06 +09:00
remph c13ecc99eb Fix linked executable segfaulting if -I and -static are both passed 2025-05-19 15:27:06 +01:00
Rui Ueyama 122268d147 Add a test 2025-05-19 16:22:07 +09:00
Rui Ueyama b990467c16 Upgrade bundled OneTBB to 2022.1.0
Specifically, we need https://github.com/uxlfoundation/oneTBB/commit/3c85ad77664f6ec1d50d9aebbd2d2f21adae0e21
to fix our ASAN bot.
2025-05-19 14:58:05 +09:00
Rui Ueyama 3d866bea61 Fix CI 2025-05-19 14:26:09 +09:00
Rui Ueyama 19a1bc651f Layout DWARF32 before DWARF64 to mitigate relocation overflow issues
Previously, if the combined size of DWARF32 and DWARF64 debug
information exceeded 2^32 bytes, a relocation overflow could occur.

Since not all static libraries are built with -gdwarf64, this overflow
issue prevented some very large programs from using static libraries
built without -gdwarf64. Recompiling everything with -gdwarf64 is the
"right" way to fix it, but that's not always feasible for various
reasons.

This commit mitigates the issue.

By sorting the input .debug_info sections so that DWARF32 debug info
comes before DWARF64, a relocation overflow can occur only if the size
of DWARF32 debug info alone exceeds 2^32 bytes.
2025-05-19 14:12:39 +09:00
Rui Ueyama 18c813a21c Make all tests pass on FreeBSD/ARM64 2025-05-16 18:06:21 +09:00
Rui Ueyama af36625a02 Fix bug where --as-needed did not work if --allow-shlib-undefined is given 2025-05-16 15:40:21 +09:00
Rui Ueyama 53c1758503 Align TLS segment correctly
This effectively reverts 5249edc57f.
2025-05-16 11:42:26 +09:00
Rui Ueyama 67ceb41b79 Refactor 2025-05-15 16:08:50 +09:00
Rui Ueyama 7d99824c3d Bump mold version to 2.39.1 2025-05-12 11:26:07 +09:00
Rui Ueyama 344dcff8e8 Change an error mesasge 2025-05-12 10:51:51 +09:00
Rui Ueyama d0dffd5985 Attempt to fix a use-after-free
https://github.com/rui314/mold/issues/1456
2025-05-11 11:12:57 +09:00
Rui Ueyama 251041e371 Fix comment 2025-05-06 11:13:42 +09:00
Rui Ueyama 4dd872ee8d Do not make a call to an unresolved weak symbol into an infinite loop
We don't do that other than RISC-V, so let's stop doing it entirely.
2025-05-06 10:41:39 +09:00
Rui Ueyama bb1b9dda64 Refactor 2025-05-06 10:26:15 +09:00
Rui Ueyama 78ec306cf4 Merge pull request #1453 from ziyao233/riscv/relax-undef-weak-sym
shrink-sections: Relax weak undef symbols if promotion is possible
2025-05-06 08:41:41 +09:00
Yao Zi 163c62f491 shrink-sections: Relax weak undef symbols if promotion is possible
A call to weak undefined symbol could be relaxed as long as the symbol
could be promoted to a dynamic symbol, where the relaxation is done
against the known PLT address and the address of the symbol itself
doesn't really matter.

Signed-off-by: Yao Zi <[email protected]>
2025-05-05 07:01:21 -04:00
Rui Ueyama 026fb54b9d Bump mold version to 2.39.0 2025-05-05 12:27:19 +09:00
Rui Ueyama cd162ba9f6 Remove an unnecessary constraint 2025-05-05 11:47:36 +09:00
Rui Ueyama e08e7f6aa4 Do not turn a call to a resolved weak undefined symbol into an infinite loop
Fixes https://github.com/rui314/mold/issues/1451
2025-05-05 11:35:03 +09:00
Rui Ueyama 6be2e676d2 Refactor 2025-05-04 20:07:24 +09:00
Rui Ueyama b6565025a3 Merge pull request #1450 from pcc/cargo
Update instructions to enable mold in Cargo on all architectures
2025-05-04 18:01:12 +09:00
Peter Collingbourne 118dc5de8e Update instructions to enable mold in Cargo on all architectures
Make it so that the Cargo instructions enable mold for Linux on all
architectures not just x86_64.
2025-05-04 01:21:20 -07:00
Rui Ueyama f3475d9f09 Refactor 2025-05-04 16:04:33 +09:00
Rui Ueyama 299904e56f Give the lowest priority to gentoo-test script 2025-05-04 14:21:55 +09:00
Rui Ueyama fded2d8a77 Remove redundant thunks after computing section addresses
Previously, we might mistakenly remove necessary symbols from thunks,
causing the linker to crash.

This fixes https://github.com/rui314/mold/issues/1449
2025-05-04 13:36:23 +09:00
Rui Ueyama f0e726024e Remove an unused function 2025-05-03 16:27:00 +09:00
Rui Ueyama 3d29f1678d Refactor 2025-05-03 11:44:50 +09:00
Rui Ueyama 559f23ff81 Give the lowest priority to gentoo-test script 2025-05-03 11:43:53 +09:00
Rui Ueyama 3274bcbdf0 Fix spurious --no-allow-shlib-undefined error
Fixes https://github.com/rui314/mold/issues/1446
2025-05-02 21:34:01 +09:00
Rui Ueyama 83dd353979 Prevent oscillation when computing the size of the phdr
Fixes https://github.com/rui314/mold/issues/1447
2025-05-01 09:34:27 +09:00
Rui Ueyama 8bae43b842 Make --default-symver work with version scripts
Previously, symbols specified with the `global:` label were unversioned
even if --default-symver was given. Now, such symbols are versioned with
the output file's soname.

Fixes https://github.com/rui314/mold/issues/1448
2025-04-30 20:45:09 +09:00
Rui Ueyama 4038cfd64d Refactor 2025-04-30 16:20:36 +09:00
Rui Ueyama 41079c64f5 [arm32be] Report error if --be8 is missing 2025-04-30 08:24:49 +09:00
Rui Ueyama 02e410133b Refactor 2025-04-29 14:34:25 +09:00
Rui Ueyama e70236a45a Refactor 2025-04-29 13:03:28 +09:00
Rui Ueyama 285c5bb509 Refactor 2025-04-29 09:12:43 +09:00
Rui Ueyama 975da12333 Fix CI 2025-04-28 22:12:05 +09:00
Rui Ueyama 157b16adb5 Support big-endian ARM32 target 2025-04-28 21:09:59 +09:00
Rui Ueyama 564757a0f5 [RV32] Do not report error on R_RISCV_32 2025-04-28 21:09:59 +09:00
Rui Ueyama 216b4a2325 Refactor 2025-04-28 15:01:56 +09:00
Rui Ueyama 954107e29c Bump mold version to 2.38.1 2025-04-28 11:10:38 +09:00
Rui Ueyama 21e20e01da Make --no-allow-shlib-undefined not report an error on circular dependencies
Fixes https://github.com/rui314/mold/issues/1445
2025-04-28 10:50:48 +09:00
Rui Ueyama 161cb6c99c Fix CI 2025-04-26 11:40:07 +09:00
Rui Ueyama 21cfee556a Transitively test library dependencies for --no-allow-shlib-undefined
This change is needed to build sci-libs/spqr-2.0.9-r4::gentoo
2025-04-26 11:30:38 +09:00
Rui Ueyama 06203c4a72 Make dist.sh reproducible for ARM32 on ARM64 2025-04-23 15:36:06 +09:00
Rui Ueyama 232067634f Simplify 2025-04-23 13:25:15 +09:00
Rui Ueyama 3f9aab7066 Build with -Wl,--gdb-index only for Debug build 2025-04-23 12:42:27 +09:00
Rui Ueyama 8b88595f26 Make dist.sh's output deterministic
It looks like `podman build` command does not push the newly created
image with the `latest` tag to ghcr.io, so the following `podman run`
pulled an older image. That caused an issue that the scirpt created
a different tarball than the one on my local machine.

I don't know what is the "proper" fix, but it looks like adding
`--pull=never` to `podman run` forbids it from pulling an older image
from ghcr.io, solving the issue.
2025-04-23 12:31:28 +09:00
Rui Ueyama fa09c562c9 Simplify 2025-04-23 11:53:24 +09:00
Rui Ueyama 9457d993c4 Upgrade Clang 2025-04-22 18:10:19 +09:00
Rui Ueyama 43810dfecd Make --no-allow-shlib-undefined on by default for executables
Fixes https://github.com/rui314/mold/issues/1443
2025-04-22 13:20:13 +09:00
Rui Ueyama 2d6061a233 Change how we resolve versioned symbols with default version
If a versioned symbol is a default one, it can be referred to with two
names, e.g. `foo` and `foo@VERSION`. Previously, we forward
`foo@VERSION` to `foo` if it is undefined, but it didn't always work
correctly. Now, we resolve both `foo` and `foo@VERSION` as usual and
then forward `foo@VERSION` to `foo` afterwards.

Fixes https://github.com/rui314/mold/issues/1169
2025-04-22 12:21:50 +09:00
Rui Ueyama 3d6aad65b0 Simplify 2025-04-21 13:55:31 +09:00
Rui Ueyama f7b1920f84 Make dist.sh reproducible again
mimalloc 2.2.2 started using __DATE__ and __TIME__ macros which make
the output non-reproducible. We can set the SOURCE_DATE_EPOCH environment
variable to fix the "current" timestamp.
2025-04-16 16:17:28 +09:00
Rui Ueyama b42f978212 Bump mold version to 2.38.0 2025-04-16 11:57:48 +09:00
Rui Ueyama ac6f1ec18e Do not hide foo@@VER if both foo@@VER and foo are defined
Fixes https://github.com/rui314/mold/issues/1442
2025-04-15 12:28:31 +09:00
Rui Ueyama d19f822ec0 Simplify 2025-04-15 11:51:11 +09:00
Rui Ueyama a97a628ee8 Resolve versioned symbols early
Fixes https://github.com/rui314/mold/issues/1440
2025-04-11 14:46:27 +09:00
Rui Ueyama 2888aade4c Refactor 2025-04-10 12:59:13 +09:00
Rui Ueyama 0172f22ab5 Refactor 2025-04-07 17:25:28 +09:00
Rui Ueyama 418d4ff62a Fix CI 2025-04-07 11:26:18 +09:00
Rui Ueyama 5249edc57f Simplify 2025-04-07 10:37:08 +09:00
Rui Ueyama 1870845f88 Refactor 2025-04-07 09:07:12 +09:00
Rui Ueyama d15c41a8dd Process --repro early
If mold crashes before --repro is processed, no .repro.tar file is
created. So we want to process the option as early as possible.
2025-04-07 08:50:26 +09:00
Rui Ueyama b7c8a843b2 Ignore --mmap-output-file
For the sake of completeness, --mmap-output-file is ignored just like
--no-mmap-output-file is.
2025-04-03 16:57:37 +09:00
Rui Ueyama 9b77a7ea86 Fix CI 2025-04-03 16:02:20 +09:00
Rui Ueyama af396ad5d9 Add --audit and --depaudit
These are for the sake of compatibility with GNU ld.
2025-04-03 15:36:05 +09:00
Rui Ueyama a2ee7371d5 Handle -mllvm as a synonym for -plugin-opt
Related to https://github.com/rui314/mold/pull/1437
2025-04-03 09:26:17 +09:00
Rui Ueyama c9812cf09f Merge pull request #1436 from christycylee/ignore_no_mmap_output_file
Ignore no-mmap-output-file for compatibility.
2025-04-03 08:59:31 +09:00
Christy Lee e9c070a3c2 Ignore no-mmap-output-file for compatibility. 2025-04-02 11:39:53 -07:00
Rui Ueyama 02e75be9fa Simplify 2025-04-02 21:55:17 +09:00
Rui Ueyama d5609a9bb8 Fix CI 2025-04-02 18:28:37 +09:00
Rui Ueyama 5d24db5a64 Handle COMDAT symbols in LTO IR object files
Before 9306f69a61, we completely ignored
duplciate symbol errors in IR object files. After that commit, we've
been accidentally reporting duplicate comdat symbols as errors.

Fixes https://github.com/rui314/mold/issues/1434
2025-04-02 17:54:41 +09:00
Rui Ueyama aeec8baee5 Revert "Take r_delta into account when computing distance"
This reverts commit b74a771886
because it broke buildbots.
2025-04-02 10:48:57 +09:00
Rui Ueyama b74a771886 Take r_delta into account when computing distance 2025-04-01 16:56:21 +09:00
Rui Ueyama 0124058ef7 Refactor 2025-03-26 20:56:04 +09:00
Rui Ueyama e302a2509c Fix CI 2025-03-26 20:56:02 +09:00
Rui Ueyama f70db66373 Use std::ranges 2025-03-26 20:26:51 +09:00
Rui Ueyama 9c0cbc8e6c Fix R_AARCH64_PREL16 range 2025-03-25 18:01:38 +09:00
Rui Ueyama a9acbd6b6f Refactor 2025-03-24 16:03:52 +09:00
Rui Ueyama b9d65a5787 Update RV32 toolchain 2025-03-24 15:40:06 +09:00
Rui Ueyama 50766f1ad4 Remove endbr32 support
It looks like Linux is going to support CET only on x86-64 (i.e. endbr64),
and it is very unlikely that x86-32 will get CET support. So I'll remove
the instruction from the code we generate for x86-32.
2025-03-23 18:07:21 +09:00
Rui Ueyama 3496ad1291 Update comments 2025-03-22 13:39:54 +09:00
Rui Ueyama 8a3d374251 Simplify 2025-03-22 11:20:07 +09:00
Rui Ueyama 96b2710849 Fix CI 2025-03-20 16:32:29 +09:00
Rui Ueyama a43f3953f0 Do not create redundant range extension thunks
Previously, we created thunks for all PLT function calls.
2025-03-20 13:25:05 +09:00
Rui Ueyama 2bc6ddc41c Refactor 2025-03-19 11:18:15 +09:00
Rui Ueyama f6575d53c1 Report an error if CREL contains addends for REL-type targets
Fixes https://github.com/rui314/mold/issues/1430
2025-03-18 10:52:55 +09:00
Rui Ueyama bf1ec5ac8f Allow very large offsets in SHT_CREL 2025-03-14 15:32:38 +09:00
Rui Ueyama b291100cfe Refactor 2025-03-14 15:05:16 +09:00
Rui Ueyama c43a859a90 Support CREL as input section
Fixes https://github.com/rui314/mold/issues/1427
2025-03-13 15:17:11 +09:00
Rui Ueyama 6e3d0e3a3c Fix build failure on Windows 2025-03-12 14:24:11 +09:00
Rui Ueyama 790ce3f64d Downgrade mimalloc to 2.2.2
mimalloc 3.0.1 cannot be compiled on Windows, so we don't want to stick
with the 2.x series for the time being.
2025-03-12 14:05:35 +09:00
Rui Ueyama cbd7f74c2e Upgrade bundled xxhash to 0.8.3 2025-03-12 13:38:26 +09:00
Rui Ueyama 3fe25fc69d Upgrade bundled mimalloc to 3.0.1 2025-03-12 13:34:39 +09:00
Rui Ueyama 823ef09585 Upgrade bundled blake3 to 1.6.1 2025-03-12 13:32:26 +09:00
Rui Ueyama f20f57060a Upgrade bundled zstd to 1.5.7 2025-03-12 11:44:12 +09:00
Rui Ueyama 0061d53409 Merge pull request #1425 from christycylee/buck_files
Remove extraneous build files
2025-03-11 10:35:56 +09:00
Rui Ueyama 82affb94d8 Fix out-of-bound memory access
Related to https://github.com/rui314/mold/issues/1426
2025-03-10 14:54:17 +09:00
Rui Ueyama 4f782fb7dc Refactor 2025-03-08 17:13:48 +09:00
Rui Ueyama e9eb6020a6 Skip if textrel is prohibited by default 2025-03-08 10:47:29 +09:00
Christy Lee 23a662cbb9 Remove extraneous build files 2025-03-07 14:23:22 -08:00
Rui Ueyama 32c8f6dd61 Bump mold version to 2.37.1 2025-03-07 14:32:02 +09:00
Rui Ueyama c95476d2fe Fix spurious "dupilcate symbol" error for LTO
Fixes https://github.com/rui314/mold/issues/1421
2025-03-07 13:17:37 +09:00
Rui Ueyama 3981f4f3d4 Bump mold version to 2.37.0 2025-03-05 15:36:27 +09:00
Rui Ueyama d4ad627919 Fix test 2025-03-05 14:22:32 +09:00
Rui Ueyama befe780cf8 Make --discard-section a hidden experimental option 2025-03-05 14:15:19 +09:00
Rui Ueyama 879fe682ee Check if TLSDESC is actually supported 2025-03-05 14:14:38 +09:00
Rui Ueyama 805a8b2b62 Install binary packages if available 2025-03-05 14:14:38 +09:00
Rui Ueyama ec360760d6 Merge pull request #1423 from christycylee/discard-section
Discard section
2025-03-05 14:14:33 +09:00
Christy Lee 30541fc1e8 Add --discard-section feature 2025-03-04 12:49:03 -08:00
Rui Ueyama 9ebd889a73 Use actions/checkout@v4 2025-03-03 17:49:59 +09:00
Rui Ueyama f15bd7537e Fix CI 2025-03-03 11:54:00 +09:00
Rui Ueyama f2ac49824c Fix CI 2025-03-03 10:45:35 +09:00
Rui Ueyama 716fb9c06d Merge pull request #1422 from LarsGullik/lto-dep-file
Remove temporary lto files from dependency-file
2025-03-03 10:45:09 +09:00
Rui Ueyama 50231607f9 Improve comments 2025-03-03 09:53:11 +09:00
Lars Gullik Bjønnes fb7a650186 Mark lto temporary files to not be a dependency
Removes the temporary file created by the compiler
from the dependency list created with --dependency-file.

Without this build systems will never report "Nothing to do",
but always do a re-link of the final object.
2025-03-03 01:10:19 +01:00
Lars Gullik Bjønnes a399548dfa Add test for lto in combination with depencency-file
There should not be any temporary files listed
as dependencies.
2025-03-03 00:39:31 +01:00
Rui Ueyama 11a62121e3 Expand a comment 2025-02-28 16:15:07 +09:00
Rui Ueyama a067b0106b Refactor 2025-02-28 16:15:07 +09:00
Rui Ueyama 7607ad6371 Refactor 2025-02-27 09:14:37 +09:00
Rui Ueyama eab8217962 Ensure that the assembler emits desired relocations 2025-02-27 09:10:13 +09:00
Rui Ueyama f46959819b Run more tests on CI 2025-02-26 17:31:38 +09:00
Rui Ueyama a17202d2b5 Support R_X86_64_CODE_6_GOTTPOFF
R_X86_64_CODE_5_{GOTPCRELX,GOTTPOFF,GOTPC32_TLSDESC} and
R_X86_64_CODE_6_{GOTPCRELX,GOTPC32_TLSDESC} are defined just for
completeness (*), so we've implemented all APX relocations with
this change.

* https://gitlab.com/x86-psABIs/x86-64-ABI/-/merge_requests/53
2025-02-26 16:29:32 +09:00
Rui Ueyama 83152acc8b Support R_X86_64_CODE_4_{GOTPCRELX,GOTTPOFF,GOTPC32_TLSDESC}
These relocations are for Intel APX (Advanced Performance Extension)
that extends the number of general-purpose registers from 16 to 32.
GNU assembler that comes with GCC 14 or newer can compile the test code.
2025-02-26 15:59:04 +09:00
Rui Ueyama 88ea3a1aa5 Update LoongArch cross toolchain 2025-02-25 19:58:50 +09:00
Rui Ueyama 0302affc11 Attempt to fix CI 2025-02-25 13:39:17 +09:00
Rui Ueyama 34986d26df Attempt to fix CI 2025-02-25 13:04:26 +09:00
Rui Ueyama 1fbbcec3da Support R_ARM_THM_JUMP8 2025-02-25 11:32:44 +09:00
Rui Ueyama 9ae7e700fa Use gcr.io instead of docker.io 2025-02-23 22:18:23 +09:00
Rui Ueyama f496c31008 Refactor 2025-02-23 21:52:59 +09:00
Christy Lee 48a84176ac Add --discard-section feature 2025-02-21 13:52:16 -08:00
Rui Ueyama 7fe4bee499 Merge pull request #1418 from christycylee/centos 2025-02-21 08:00:36 +09:00
Christy Lee 015d5388a0 Allow installing dependencies on centos 2025-02-20 13:39:04 -08:00
Rui Ueyama 9306f69a61 Report a "symbol duplication" error for LTO
Fixes https://github.com/rui314/mold/issues/1414
2025-02-17 17:03:42 +09:00
Rui Ueyama b82028d45d Merge pull request #1277 from perseoGI/conan/update-readme 2025-02-13 19:43:17 +09:00
PerseoGI 123faa82dc Update readme redirecting to conan website with mold instructions 2025-02-13 11:23:14 +01:00
Rui Ueyama 93f540a10a Use x86 GitHub runners
It looks like Qemu on ARM64 is unstable on ARM64 runners. I observed
occasional failures with SEGV.
2025-02-13 09:31:32 +09:00
Rui Ueyama 143d447a66 Take sentinel into account when estimating section size 2025-02-09 14:34:56 +09:00
Rui Ueyama 8b089859ee Improve tests 2025-02-09 14:22:54 +09:00
Rui Ueyama 4b754fd944 [ARM32] Emit the sentinel in .ARM.exidx 2025-02-09 13:40:07 +09:00
Rui Ueyama b9f938e801 Improve tests 2025-02-09 11:47:12 +09:00
Rui Ueyama 6e50f3a5e7 Remove a redundant test 2025-02-09 11:38:48 +09:00
Rui Ueyama c2df589e05 Skip a test 2025-02-09 11:18:05 +09:00
Rui Ueyama 247ae9500c Fix tests 2025-02-09 10:43:34 +09:00
Rui Ueyama 85e8d4179e Use ARM64 runners because they are faster than x86 runners
I believe this is just due to the lower number of users on ARM64
runners compared to x86. However, based on my measurements, ARM64
builds all binaries in 1h30m, whereas x86 takes 2h30m.
2025-02-09 10:14:05 +09:00
Rui Ueyama 9185ee4b05 [PPC64V1] Garbage-collect text sections 2025-02-09 10:11:45 +09:00
Rui Ueyama 16f75990f9 [ARM32] Garbage-collect .ARM.exidx sections 2025-02-09 10:11:45 +09:00
Rui Ueyama 34053cc52e Fix tests 2025-02-09 10:11:45 +09:00
Rui Ueyama 810946130d Collect generated files on test failure 2025-02-09 08:48:18 +09:00
Rui Ueyama 33b6f3dee9 Rename a test 2025-02-09 07:35:44 +09:00
Rui Ueyama 14a276ecde Set pipefail to test rigorously
We need to stop using `grep -q` because grep terminates immediately after
finding the first match with `-q`, causing SIGPIPE to the process on the
other side of the pipe.
2025-02-09 07:33:54 +09:00
Rui Ueyama 245d265b4b Simplify 2025-02-08 19:28:39 +09:00
Rui Ueyama 0619541234 Simplify 2025-02-08 18:41:01 +09:00
Rui Ueyama 8f57d2affd Simplify 2025-02-08 18:15:07 +09:00
Rui Ueyama 501f66b24c Report duplicate symbol errors even if --gc-sections is given
Fixes https://github.com/rui314/mold/issues/1414
2025-02-08 13:50:06 +09:00
Rui Ueyama 6ef9ffb81a Fix CI 2025-02-08 13:50:06 +09:00
Rui Ueyama 9ab2c31086 Improve tests 2025-02-08 13:35:43 +09:00
Rui Ueyama 182bd7b68c Refactor 2025-02-08 13:00:08 +09:00
Rui Ueyama 09e205d11e Make $plt symbols local
Related to https://github.com/rui314/mold/issues/1413
2025-02-08 11:46:23 +09:00
Rui Ueyama 7e5e396aae Add a missing entry 2025-02-07 23:14:27 +09:00
Rui Ueyama b8914d0a94 Avoid cache line false sharing 2025-02-07 09:46:20 +09:00
Rui Ueyama 359e5134c9 Refactor 2025-02-05 22:16:03 +09:00
Rui Ueyama 4ed6f48f55 Refactor 2025-02-05 18:53:11 +09:00
Rui Ueyama 48beed9fdf Refactor 2025-02-05 18:20:10 +09:00
Rui Ueyama 42cc55e6d9 Update mold.1 (automated commit) 2025-02-03 02:33:40 +00:00
Rui Ueyama 04103ae60a Fix typo 2025-02-03 11:24:09 +09:00
Rui Ueyama 0966f1858c Refactor 2025-02-03 11:24:05 +09:00
Rui Ueyama 56748ed199 Refactor 2025-02-02 18:54:27 +09:00
Rui Ueyama 11387b3c19 Fix relocations for the large code model 2025-02-02 18:53:46 +09:00
Rui Ueyama 04d71affd1 Define __rela_iplt_{start,end} as absolute symbols if not necessary
Previously, these symbols were defined relative to a section. That is
logically correct, because the runtiem is supposed to apply IREL
relocs between `__rela_iplt_start` and `__rela_iplt_end`, so their
actual locations shouldn't matter if there's no IREL relocs (i.e. they
refer to the same location.)

However, under some circumstances, glibc reads `__rela_iplt_start`
before applying base relocations and `__rela_iplt_end` after applying
them due to compiler instruction reordering.

Therefore, we need to make them absolute symbols that don't require
base relocations.

Fixes https://github.com/rui314/mold/issues/1407
2025-02-02 10:54:33 +09:00
Rui Ueyama 6cb2f31236 Refactor 2025-02-02 10:54:33 +09:00
Rui Ueyama 01392bd53a Use Debian 9 as the base image
It's year of 2025, so a distro from 2017 should be old enough to build
our binary distribution.
2025-01-31 20:25:14 +09:00
Rui Ueyama 7cb0b817f5 Fully quailify container image URLs for better compatibility 2025-01-31 16:00:03 +09:00
Rui Ueyama ea38c1544f Upgrade upload-artifcat action 2025-01-31 11:23:14 +09:00
Rui Ueyama 80a2d24948 Compress debug sections in background
If both --separate-debug-info and --compress-debug-info are given, we want to
compress debug sections in background so that the delay caused by compression
is not visible to users.
2025-01-31 10:26:35 +09:00
Rui Ueyama ded9292ba9 Merge pull request #1409 from 0x2Adr1/compress_debug_sections_separate_debug_file
Honor --compress-debug-sections with --separate-debug-file
2025-01-31 10:21:39 +09:00
Adrien Garin 6d1c96f34b Add --no-detach 2025-01-30 07:31:00 -06:00
Rui Ueyama 0849789fd5 Build loongarch64 binaries 2025-01-30 18:06:31 +09:00
Rui Ueyama 7f771d6ed9 Fix test 2025-01-30 18:05:56 +09:00
Rui Ueyama b8a46ede02 Merge pull request #1406 from sicherha/skip_s390x_tests_with_buggy_gcc
[ELF][S390X] Skip tests that fail due to buggy code produced by GCC
2025-01-30 18:03:46 +09:00
Adrien Garin bab7dd1b2e Honor --compress-debug-sections with --separate-debug-file 2025-01-29 11:54:46 -06:00
Rui Ueyama ebe887f0ae Update README 2025-01-29 13:07:01 +09:00
Rui Ueyama df30516630 Unbreak build-all buildbot 2025-01-29 10:07:28 +09:00
Rui Ueyama e8101a4d8a Use the ARM64 builder to build ARM32 binary
I didn't know if Azure ARM64 servers support ARM32 instructions, but
apparently they do because running ARM32 code is much faster on ARM64
GitHub Actions runners.
2025-01-29 08:37:52 +09:00
Rui Ueyama 6291f1b86c Add an entry for Rocky Linux 2025-01-28 11:34:59 +09:00
Christoph Erhardt a570e8953c [ELF][S390X] Skip tests that fail due to buggy code produced by GCC
`bno-symbolic` and `canonical-plt` still fail with GCC 15.
`rodata-name` started failing with GCC 14.
2025-01-24 21:03:07 +01:00
Rui Ueyama 5cc0e9d0a1 Build ARM64 binary on push 2025-01-24 09:47:34 +09:00
Rui Ueyama 4bb8e03b24 Build an ARM64 binary on a native ARM64 host
This change speeds up building an ARM64 binary on a GitHub Actions runner.
2025-01-22 20:56:10 +09:00
Rui Ueyama bcadb4e30f Refactor 2025-01-21 13:43:28 +09:00
Rui Ueyama a2eda6acc4 [RISC-V] Do not leave a useless ld instruction
ld may cause a fault at runtime, so we want to make sure that that is
replaced with a nop if relaxed.
2025-01-21 13:25:09 +09:00
Rui Ueyama e60e04feb2 Fix Windows build
Cherrypick https://github.com/microsoft/mimalloc/commit/5764845c4d3d9fe77ae3c2fbfcc2beedce368b1d
to fix Windows CI.
2025-01-21 08:01:50 +09:00
Rui Ueyama 3651dba6ef Upgrade bundled mimalloc to 2.1.9 2025-01-20 20:18:10 +09:00
Rui Ueyama c20d027441 Refactor 2025-01-20 20:18:10 +09:00
Rui Ueyama 66b5ed66c6 [ARM32] Increase the default page size to 64 KiB
... to maximize compatibility with broader hardware.
2025-01-20 20:16:38 +09:00
Rui Ueyama 0b747d8334 Merge pull request #1404 from kei-gnu/upgrade-checkout-actions
Upgrade checkout actions
2025-01-20 18:49:48 +09:00
kei-gnu 73f86aeadd upgrade checkout actions 2025-01-20 10:52:05 +09:00
Rui Ueyama ceee495584 Fix R_ARM_THM_JUMP19 2025-01-20 09:22:58 +09:00
Rui Ueyama 3d89c1181b Refactor 2025-01-19 21:31:34 +09:00
Rui Ueyama f014a1d015 Read R_ARM_THM_JUMP19 relocation addends 2025-01-19 21:27:01 +09:00
Rui Ueyama f25e450f2b Show "LTO plugin missing" error correctly
Fixes https://github.com/rui314/mold/issues/1403
2025-01-19 12:09:42 +09:00
Rui Ueyama 1fbc6f3dec Refactor 2025-01-19 10:38:38 +09:00
Rui Ueyama 4679573654 Refactor 2025-01-19 10:32:40 +09:00
Rui Ueyama af608f5651 Fix CI 2025-01-17 23:06:07 +09:00
Rui Ueyama 5a9c465210 Fix CI 2025-01-17 21:41:41 +09:00
Rui Ueyama 1822e47a43 Make -z dynamic-undefined-weak work for executable
Fixes https://github.com/rui314/mold/issues/1401
2025-01-17 19:44:40 +09:00
Rui Ueyama 06f20e9d69 Add new x86-64 relocations
These relocations are for Intel Advanced Performance Extensions.
The real implementation will be added once major compilers start
supporting APX.
2025-01-17 08:49:06 +09:00
Rui Ueyama 8929bd4a8d Fix test
Fixes https://github.com/rui314/mold/issues/1400
2025-01-17 08:40:58 +09:00
Rui Ueyama 3b1e2a9aa3 Rename a function 2025-01-15 11:22:11 +09:00
Rui Ueyama 721eafc7dd Refactor 2025-01-15 09:38:55 +09:00
Rui Ueyama 4de8daf34f Fix typo 2025-01-15 09:38:55 +09:00
Rui Ueyama f7624ee1f9 Simplify 2025-01-15 08:15:35 +09:00
Rui Ueyama 500416f8f6 Improve test 2025-01-15 07:50:56 +09:00
Rui Ueyama c6c12d43f5 Fix CI 2025-01-14 14:08:22 +09:00
Rui Ueyama 7fc2a6e935 Fix CI 2025-01-14 13:25:32 +09:00
Rui Ueyama 9fe8831db3 Fix CI 2025-01-14 12:41:29 +09:00
Rui Ueyama 2bb42badde Do not emit bad dynamic relocations when sections are shrunk 2025-01-14 11:42:40 +09:00
Rui Ueyama 48077dd91a Refactor 2025-01-11 21:28:01 +09:00
Rui Ueyama cf0d750734 Set the basename of an output path to .gnu.version_d 2025-01-11 19:49:36 +09:00
Rui Ueyama 48dd767ad3 Refactor 2025-01-11 19:47:50 +09:00
Rui Ueyama 3c89ad66a2 Refactor 2025-01-11 17:44:55 +09:00
Rui Ueyama 8cd5574bfb Use shrink_to_fit instead of = {} assignment
I'm no C++ expert, but it looks like `vec = {}` is not guaranteed to
release memory.
2025-01-11 14:01:10 +09:00
Rui Ueyama 0ca543e104 Optimize create_output_section() 2025-01-11 13:12:53 +09:00
Rui Ueyama e5f80fd822 Refactor 2025-01-11 13:12:53 +09:00
Rui Ueyama fe8e061427 Refactor
This new logic was found by ChatGPT o1 pro.
2025-01-10 17:34:00 +09:00
Rui Ueyama 05ceb7da0d Revert "Build daily binary for loongarch64"
This reverts commit 12a141b03b to unbreak
buildbots.
2025-01-10 17:26:34 +09:00
Rui Ueyama 3cec2e68be Upgrade Clang 2025-01-10 10:50:44 +09:00
Rui Ueyama 12a141b03b Build daily binary for loongarch64 2025-01-10 09:37:43 +09:00
Rui Ueyama 562361345b Use the latest version of clang 2025-01-10 08:09:58 +09:00
Rui Ueyama 432ffeabf5 Fix typo 2025-01-10 07:59:05 +09:00
Rui Ueyama be0870719f Check if test system supports TLSDESC 2025-01-09 22:12:24 +09:00
Rui Ueyama a66750b1ec Update README.md 2025-01-09 21:06:26 +09:00
Rui Ueyama c3a957c4dc Bump mold version to 2.36.0 2025-01-09 12:53:32 +09:00
Rui Ueyama f193d2b603 Format 2025-01-09 12:53:32 +09:00
Rui Ueyama d2859f597c Simplify 2025-01-09 11:51:58 +09:00
Rui Ueyama 06a21efbdb Improve a test 2025-01-08 17:40:52 +09:00
Rui Ueyama 13aa1685c9 Update mold.1 (automated commit) 2025-01-08 07:00:45 +00:00
Rui Ueyama 55fcd3facf Document --package-metadata 2025-01-08 15:58:11 +09:00
Rui Ueyama 5d454308f2 Remove BitVector and use std::vector<bool>
Even though std::vector<bool> is often critisized for being inconsistent
with other std::vector<T> where T != bool, it suffices for our purpose.
2025-01-08 13:51:21 +09:00
Rui Ueyama a01fe171d0 Refactor 2025-01-08 11:11:16 +09:00
Rui Ueyama 207c4e99f0 Pass --pids-limit=-1 to podman
Without it, fork(2) may fail with EAGAIN inside podman containers.
2025-01-07 16:13:48 +09:00
Rui Ueyama 0dfdc014e2 Change the path of mold binary 2025-01-07 13:24:06 +09:00
Rui Ueyama eec3f6b716 [RISC-V] Fix R_RISCV_HI20 relaxation
We didn't compensate LO12's sign extension when computing the range.
This resulted in a wrong relaxation that breaks the output binary.
2025-01-07 11:41:22 +09:00
Rui Ueyama a483530a3c Refactor 2025-01-07 11:41:22 +09:00
Rui Ueyama 0d1068e460 Refactor 2025-01-06 22:31:06 +09:00
Rui Ueyama fd0fc9a3cf Refactor 2025-01-06 22:27:33 +09:00
Rui Ueyama 40b881b7bb Simplify 2025-01-06 16:41:48 +09:00
Rui Ueyama 35cde381b7 Refactor 2025-01-06 15:41:25 +09:00
Rui Ueyama 6d8c5ca90b Fix symbol size computation 2025-01-06 13:50:15 +09:00
Rui Ueyama aa4d065fde Do not call get_r_delta() unless necessary
get_r_delta performs a binary search on a vector, so it's not cheap.
If we can avoid calling that function, we should.
2025-01-06 13:36:24 +09:00
Rui Ueyama 0af930ae28 Refactor 2025-01-06 11:07:00 +09:00
Rui Ueyama d29fabf084 Refactor 2025-01-05 22:40:46 +09:00
Rui Ueyama 2ee1d47ca8 Do not add useless entries to r_deltas 2025-01-05 20:52:59 +09:00
Rui Ueyama 8e27616934 Include the number of code-shrinking relocations into the output of `--stats' 2025-01-05 20:12:31 +09:00
Rui Ueyama a7b8c6061a Ensure that the first byte of .dynsym is '\0' 2025-01-05 19:48:46 +09:00
Rui Ueyama aef5f3231c Refactor 2025-01-05 15:31:25 +09:00
Rui Ueyama 38ae2990a0 Refactor 2025-01-05 14:53:50 +09:00
Rui Ueyama e6345d5b14 Reflect the result of code-shrinking relaxation to symbol sizes 2025-01-05 13:49:07 +09:00
Rui Ueyama ba35344bd0 Add a comment 2025-01-05 12:50:50 +09:00
Rui Ueyama c33ca0b001 Refactor 2025-01-05 11:09:45 +09:00
Rui Ueyama 77935f1807 Accept very large input text sections 2025-01-04 22:11:24 +09:00
Rui Ueyama 3234d88600 [RISC-V][LoongArch] Reduce memory usage for shrinking sections
Previously, 4 bytes were allocated for each relocation in a text
section to store the number of bytes removed before each relocation.
Now, only the locations where bytes are removed are stored.

This commit improves performance by ~4% when linking clang-19 for RISC-V.
2025-01-04 21:12:02 +09:00
Rui Ueyama 1e8cee85bb Name each thunk symbol unique name
Previously, if symbol `foo` appears more than once in multiple thunks,
they got the same name `foo$thunk`. Now, they are named `foo$thunk0`,
etc.
2025-01-03 17:37:17 +09:00
Rui Ueyama 2352ec647d Fix a range extension thunk creation issue
Previously, we failed to create range extension thunks if we have
a very large input section.
2025-01-03 17:37:17 +09:00
Rui Ueyama 88d5e74825 Refactor 2025-01-03 17:37:17 +09:00
Rui Ueyama d884620f9d Attempt to fix buildbots 2025-01-03 14:28:04 +09:00
Rui Ueyama e55363ec8a Attempt to fix CI 2025-01-03 13:32:00 +09:00
Rui Ueyama a297859c53 Emit an IRELATIVE relocation instead of GLOB_DAT for an ifunc in DSO
Fixes https://github.com/rui314/mold/issues/1385
2025-01-03 12:53:34 +09:00
Rui Ueyama 0a112d0aa9 Refactor 2025-01-03 10:52:37 +09:00
Rui Ueyama 6a342f2c64 Refactor 2025-01-03 09:15:55 +09:00
Rui Ueyama d0bcd35bf5 Add a safety margin when computing branch offsets 2025-01-02 21:34:47 +09:00
Rui Ueyama f609510e3c Refactor 2025-01-02 21:21:25 +09:00
Rui Ueyama 9fc0ace817 Reduce memory usage of range extension thunks
Previously, we allocate 4 byte struct for each relocation to store a
reference to a thunk in case the relocation needs to branch to a thunk.
Most references are null because most relocations don't need thunks.

This commit changes how we manage references to range extension thunks.
Now, thunk addresses are stored to symbols rather than to relocations.

Linking clang-19 for ARM64 is now ~7% faster than before.
2025-01-02 18:09:45 +09:00
Rui Ueyama 89f3ba89da Refactor 2025-01-02 12:05:00 +09:00
Rui Ueyama 09e7616186 Refactor 2025-01-01 08:43:38 +09:00
Rui Ueyama 2342938cea Simplify 2024-12-31 21:43:28 +09:00
Rui Ueyama 047675b762 Unbreak ARM32 buildbot 2024-12-31 17:15:17 +09:00
Rui Ueyama 531e43eddf Do not mark DSOs as needed if they are referenced only indirectly
Fixes https://github.com/rui314/mold/issues/1395
2024-12-31 17:15:15 +09:00
Rui Ueyama 50a75c25e3 Refactor 2024-12-31 17:15:14 +09:00
Rui Ueyama 7a69bd45ac Refactor 2024-12-30 23:18:25 +09:00
Rui Ueyama 006de74706 Merge .rodata.cst.<symbol-name>.cst<N> sections to .rodata.cst<N> 2024-12-30 14:43:09 +09:00
Rui Ueyama 952fdde7ae Add a relocation overflow check 2024-12-30 11:12:17 +09:00
Rui Ueyama dfce2fc8fa [s390x] Reserve _GLOBAL_OFFSET_TABLE_[2]
ELF Application Binary Interface s390x Supplement Version 1.6.1, p.58 says
that "The doublewords at _GLOBAL_OFFSET_TABLE_[1...2] are reserved for
system use."

We had previously reserved GOT[0] and GOT[1], but we did not reserve GOT[2].

It's most likely that GOT[2] is not actually in use, as it appears that
mold-linked programs work without it. But we need to adhere to the psABI.
2024-12-30 09:05:10 +09:00
Rui Ueyama 50fd4beb3e [s390x] Relax GOT-loading LGRL instructions 2024-12-30 07:55:55 +09:00
Rui Ueyama 0b8090994b Simplify 2024-12-29 10:02:29 +09:00
Rui Ueyama 37e6d2f4cb Merge pull request #1394 from sicherha/fix_ifunc-alias_test_on_RHEL10
Fix `arch-x86_64-ifunc-alias` test on RHEL 10
2024-12-28 19:26:21 +09:00
Christoph Erhardt 0c06bef82b Fix arch-x86_64-ifunc-alias test on RHEL 10
On RHEL 10, GCC is configured with the `--with-arch_64=x86-64-v3`
option, which makes it implicitly use the `-march=x86-64-v3`
command-line flag by default.

This breaks the `arch-x86_64-ifunc-alias` test: since the `x86-64-v3`
microarchitecture level includes SSSE3 and AVX2, GCC appears to
interpret the `default` and `ssse3,avx2` targets as the same thing, and
omits generating a resolver function.

Fix the test by forcing GCC's target architecture to the `x86-64`
baseline.
2024-12-28 10:47:07 +01:00
Rui Ueyama 53dbfd96de Make Ctrl-C work for dist.sh 2024-12-28 18:40:15 +09:00
Rui Ueyama 938f145150 Attempt to fix CI 2024-12-27 22:35:00 +09:00
Rui Ueyama 930ba5a7f1 Do not compress .tar.gz with zip 2024-12-27 22:35:00 +09:00
Rui Ueyama 4107c336a8 Simplify 2024-12-27 22:15:25 +09:00
Rui Ueyama d4db31ceaf Merge pull request #1389 from sicherha/improvement_lambdas_and_ranges
Small improvements: lambdas and ranges
2024-12-27 19:33:03 +09:00
Rui Ueyama 30f68106db Merge pull request #1391 from sicherha/debian_11_CI_pipeline
Add CI pipeline for Debian 11
2024-12-27 19:32:24 +09:00
Rui Ueyama e56fc35820 Attempt to unbreak GitHub Actions 2024-12-27 19:27:36 +09:00
Rui Ueyama b8fa19bf24 Merge pull request #1392 from chinrw/dev
Try again to use Podman instead of Docker
2024-12-27 18:30:27 +09:00
Ruowen Qin d9bd1468d0 Try again to use Podman instead of Docker
Switch from Docker to Podman for container builds and remove unused
buildx steps. Also this commit updates GitHub Actions workflows to use
redhat-actions/podman-login and docker/setup-qemu-action@v3, removing
references to Docker Buildx.

Fixes: ec1d7120aa ("Use docker only on GitHub Actions")
Signed-off-by: Ruowen Qin <[email protected]>
2024-12-27 17:11:22 +08:00
Christoph Erhardt b5dfab17da Add CI pipeline for Debian 11
Debian 11 ships GCC 10 and CMake 3.18, which happen to be precisely the
minimum versions required for building mold from source.

Add a CI pipeline to prevent future commits from inadvertently breaking
compatibility with older toolchains.
2024-12-27 08:50:42 +01:00
Christoph Erhardt 7d1c970a39 Make non-capturing lambdas actually non-capturing 2024-12-27 08:37:48 +01:00
Rui Ueyama e6e1cfac31 Handle "L0^A" symbol as local
I don't know about the details, but it looks like "L0^A" is sometimes
used as a local symbol name.
2024-12-27 16:35:43 +09:00
Rui Ueyama 38bca63747 Make the source directory read-only for the build script
So that even a malicious container cannot silently modify our
source files.
2024-12-27 06:29:01 +09:00
Rui Ueyama 606ce43c5b Add loongarch64 support 2024-12-27 06:15:29 +09:00
Rui Ueyama 41413f9d7c Refactor 2024-12-26 20:42:39 +09:00
Rui Ueyama ec1d7120aa Use docker only on GitHub Actions
I don't know how to fix the build breakage on GitHub, so let's keep
using Docker there.
2024-12-26 19:56:26 +09:00
Rui Ueyama bcce7091e7 Run build-x86 GitHub Action on push to any branch 2024-12-26 18:03:55 +09:00
Rui Ueyama e3cb2aa09e Use podman instead of docker
I prefer podman over docker because podman works without the daemon
and doesn't need the root priviledge.
2024-12-26 17:10:03 +09:00
Rui Ueyama 7e7d75fb4b Format 2024-12-26 17:08:52 +09:00
Rui Ueyama e333e435bd Merge pull request #1388 from sicherha/improvement_cleanup
Miscellaneous small cleanups
2024-12-26 17:08:43 +09:00
Christoph Erhardt afd2fab630 Replace putenv() with setenv()
The use of `putenv()` is discouraged for portability reasons:
https://pubs.opengroup.org/onlinepubs/9799919799/functions/putenv.html
2024-12-25 19:17:17 +01:00
Christoph Erhardt 26493c0ca8 Replace stat() with equivalent std::filesystem calls 2024-12-25 19:17:17 +01:00
Christoph Erhardt 9402ca357c Construct std::vector elements in place
This is slightly more efficient than creating the objects and
subsequently copying them into the vector.
2024-12-25 19:17:17 +01:00
Christoph Erhardt 5572679c2d Remove duplicate if-case
Reported by clang-tidy: the same code already exists 50 lines below this
one.
2024-12-25 13:49:42 +01:00
Rui Ueyama 179d5d4b5c Merge pull request #1387 from sicherha/improvement_includes 2024-12-25 20:32:40 +09:00
Christoph Erhardt f169f1c45c Reorganise TBB #includes
Add missing `#include`s and remove unused ones.
2024-12-25 11:41:04 +01:00
Christoph Erhardt e05f3b5362 Include the C++ variant of C headers 2024-12-25 11:41:02 +01:00
Christoph Erhardt a5b3a22517 Remove unused #includes 2024-12-25 11:40:29 +01:00
Rui Ueyama 82263dce4d Merge pull request #1386 from jinghao-jia/main
Restore executable overwriting behavior on Linux 6.13 or later
2024-12-25 18:12:38 +09:00
Jinghao Jia 1a012dda4d Restore executable overwriting behavior on Linux 6.13 or later
Linux upstream has reverted commit 2a010c412853 ("fs: don't block
i_writecount during exec") and the change is merged in the 6.13 cycle.
This means the executable overwriting behavior can be restored on Linux
kernel version 6.13 or later.

Note that there is probably not much we can do for 6.11 / 6.12 kernels.
Although the reversion is backported, many distros do not use upstream
sub-version numbers which means we cannot determine whether the kernel
has this reversion based on its version number.

Signed-off-by: Jinghao Jia <[email protected]>
2024-12-25 01:55:07 -06:00
Rui Ueyama dfe02f41cb Add a test 2024-12-24 17:16:34 +09:00
Rui Ueyama b582ad3589 Format 2024-12-24 17:16:32 +09:00
Rui Ueyama 2d7b6b23e3 [LoongArch] Increase the max page size from 16 KiB to 64 KiB
Even though 16 KiB is the common page size, the Linux kernel for
LoongArch supports 64 KiB pages. Currently, mold-generated binaries
run only on systems with <=16 KiB page.

Note that unlike GNU ld, increasing the maximum page size doesn't
increase the on-disk binary size of generated binaries.
2024-12-22 17:44:53 +09:00
Rui Ueyama 7ddc8f4654 Rename --encoded-package-metadata to --package-metadata
This is an alternative to https://github.com/rui314/mold/pull/1383

The upcoming version of GNU binutils's ld now treats arguments for
`--package-metadata` as percent-encoded strings and decodes them before
writing to .note.package.

Additionally, GNU ld will support `%[<charname>]`, where `<charname>` can be
`comma`, `lbrace`, `rbrace`, `quot`, or `space`. This feature could improve
readability when manually writing command-line arguments.

That said, I don't believe we need to support these metacharacters, for the
following reasons:

1. This option is specifically designed for distribution package maintainers
   to embed metadata into binaries, and is not intended for general use.

2. Command-line arguments for this option are always generated by build
   scripts, not written manually.

If we need these character names, we can add that later.

Furthermore, GNU ld does not appear to report errors for orphan '%'
characters that are not followed by valid hex values; instead, it simply
passes through such characters. I find this behavior fragile. We will report
an error for invalid '%' characters.

With these considerations in mind, I'll just rename the existing
`--encoded-package-metadata` option to `--package-metadata`.
2024-12-22 10:07:23 +09:00
Rui Ueyama 4bbd93f06f Refactor 2024-12-22 10:07:23 +09:00
Rui Ueyama a76d259c0b Merge pull request #1384 from bdrung/ctest-with-logs 2024-12-22 05:27:57 +09:00
Benjamin Drung a557ea1012 ci: run ctest with --output-on-failure
In case ctest fails in the CI, no error logs are printed. This makes
debugging harder.

So run ctest with `--output-on-failure` to ease debugging in case of
failures.
2024-12-21 20:37:31 +01:00
Rui Ueyama 06f6a56043 Merge pull request #1380 from sicherha/fix_system_mimalloc
Add missing `#include "common.h"` - fixes broken `MOLD_USE_SYSTEM_MIMALLOC`
2024-12-20 10:39:39 +09:00
Christoph Erhardt ac9568f17b Disable mimalloc when a sanitizer is used
Combining mimalloc and AddressSanitizer triggers a segmentation fault in
many tests.

Combining mimalloc and ThreadSanitizer leads to duplicate symbols for
the `new` and `delete`
operators, causing the build to fail.
Example message from a failed GitHub Actions build:
```
mold: error: duplicate symbol:
third-party/mimalloc/libmimalloc-debug.a(alloc.c.o):
/usr/lib/llvm-18/lib/clang/18/lib/linux/libclang_rt.tsan_cxx-x86_64.a(tsan_new_delete.cpp.o):
operator delete(void*, std::align_val_t)
```
2024-12-20 00:03:47 +01:00
Christoph Erhardt 9c9145eb6e Add missing #include "common.h"
This allows the compiler to make sure that the implementation of
`set_mimalloc_options()` matches its declaration.

More importantly, it indirectly pulls in `config.h`, where the macros
`MOLD_USE_SYSTEM_MIMALLOC` and `MOLD_USE_MIMALLOC` are conditionally
defined. Without these, the build configuration is ignored.
2024-12-19 22:06:23 +01:00
Rui Ueyama 3cb551424b Fix CI 2024-12-16 17:27:33 +09:00
Rui Ueyama ee8e50a3f7 Bump mold version to 2.35.1 2024-12-15 13:26:50 +09:00
Rui Ueyama 4bbf85c2ea Refactor 2024-12-13 11:13:06 +09:00
Rui Ueyama 6257f12c1f Simplify 2024-12-12 12:45:53 +09:00
Rui Ueyama ea460af09c Fix function prototypes
Fixes https://github.com/rui314/mold/issues/1370
2024-12-12 12:13:29 +09:00
Rui Ueyama a0f9641ae1 Upgrade BLAKE3 to 1.5.5 2024-12-11 20:13:03 +09:00
Rui Ueyama 9204e1dc12 Upgrade OneTBB to 2022.0.0 2024-12-11 20:13:03 +09:00
Rui Ueyama 396666fd48 Preserve the original relocation order as much as possible 2024-12-11 20:13:03 +09:00
Rui Ueyama 74024ea0df Refactor 2024-12-11 15:22:22 +09:00
Rui Ueyama 79aac78581 Refactor 2024-12-09 18:37:01 +09:00
Rui Ueyama 04066d1c1a [RISC-V] Remove GP-relative relocations
These relocations were deleted from the psABI in
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/commit/ad025461defeb610c684427719ce5ac5e8d8d857.
2024-12-09 18:33:30 +09:00
Rui Ueyama 2a78b1b333 Make ICF deterministic
Fixes https://github.com/rui314/mold/issues/1377
2024-12-09 15:13:20 +09:00
Rui Ueyama 27162691b7 Bump mold version to 2.35.0 2024-12-08 14:58:53 +09:00
Rui Ueyama fe6090efff Revert "Upgrade RV32 toolchain"
This reverts commit c7042f416e because it broke CI.
2024-12-05 18:29:47 +09:00
Rui Ueyama c7042f416e Upgrade RV32 toolchain 2024-12-05 16:44:09 +09:00
Rui Ueyama e56b649968 Increase the maximum retry count for our concurrent hashmap
Fixes https://github.com/rui314/mold/issues/1365
2024-12-05 14:17:56 +09:00
Rui Ueyama 0f56a792c3 Upgrade LoongArch toolchain 2024-12-04 16:06:01 +09:00
Rui Ueyama 5bb914553f Fix typo 2024-11-26 13:21:41 +09:00
Rui Ueyama 26d898e9bd Use macos-latest instead of macos-12
macos-12 has been deprecated.
2024-11-26 07:45:07 +09:00
Rui Ueyama eec3bb4478 Simplify 2024-11-26 07:01:50 +09:00
Rui Ueyama fa3026611b Merge pull request #1373 from bl4ckb0ne/test-x86-variants
test: match more x86 arch name variants
2024-11-26 06:59:21 +09:00
Simon Zeni a649156e0f test: match more x86 arch name variants 2024-11-25 11:06:25 -05:00
Rui Ueyama e2e1146a0b [s390x] Fix .hash section
For whatever reason, the entry size for .hash is 8 bytes insetad of 4 on s390x.

Fixes https://github.com/rui314/mold/issues/1372
2024-11-23 13:20:07 +09:00
Rui Ueyama baf4f76f53 Report an error even without NDEBUG=1 if a ConcurrentMap becomes full 2024-11-02 17:05:01 +09:00
Rui Ueyama 85be067813 Try to silence benign mimalloc warning messages
https://github.com/rui314/mold/issues/1367
2024-10-30 13:29:57 +09:00
Rui Ueyama 8e4f7b5383 Do not ovewrite an existing file if running on Linux 6.11.0 or later
https://github.com/rui314/mold/issues/1361
2024-10-29 16:33:45 +09:00
Rui Ueyama a066efa995 Use madvise(2) if available 2024-10-29 16:01:04 +09:00
Rui Ueyama f184021eca Refactor 2024-10-27 16:42:36 +09:00
Rui Ueyama 0841ffcb20 Refactor 2024-10-20 11:33:38 +09:00
Rui Ueyama 9cf1165a34 Fix CI 2024-10-19 11:33:06 +09:00
Rui Ueyama 9aa4ded614 Remove LICENSE.third-party
We don't need to create a copy of license files for third-party software
as we keep an original license file for each subdirectory. It is probably
a bit risky to create copies of license files because it is easy to become
out-of-sync. So I'll just remove this file.
2024-10-19 11:08:12 +09:00
Rui Ueyama e7e764ccf0 Revert "Refactor"
This reverts commit 5ba5b4960f to fix CI.
2024-10-19 09:47:53 +09:00
Rui Ueyama 5ba5b4960f Refactor 2024-10-18 20:57:07 +09:00
Rui Ueyama bb57289385 Refactor 2024-10-18 18:46:23 +09:00
Rui Ueyama 0e28522b5c Fix filler instruction for sh4be
If running in big-endian mode, SH4 processors expects both data and
instructions to be encoded in big-endian. Therefore, nop need to be
reversed for sh4be.
2024-10-17 16:13:46 +09:00
Rui Ueyama 882e7eb15b Support big-endian ARM64 2024-10-17 12:27:12 +09:00
Rui Ueyama 3a0bb87dfe Refactor 2024-10-17 12:27:10 +09:00
Rui Ueyama 832973cef5 Run a test for -z rewrite-endbr on GitHub Actions 2024-10-16 16:22:51 +09:00
Rui Ueyama 0cb9fc613c Support big-endian SH4
This commit adds experimental big-endian SH4 support. Since we already
support little-endian SH4, it wasn't too difficult to support its big-
endian variant.

Some tests related to C++ exceptions fail due to mysterious errors.
I don't have time to debug it, so I'll disable them for now.

Closes https://github.com/rui314/mold/issues/1273
2024-10-15 14:23:54 +09:00
Rui Ueyama 8931cf81f4 Update mold.1 (automated commit) 2024-10-15 04:49:05 +00:00
Rui Ueyama ecdd0f71b6 Update the man page 2024-10-15 13:48:07 +09:00
Rui Ueyama cb9344fab8 Run tests 2024-10-15 13:48:05 +09:00
Rui Ueyama 382e45ea30 Align .gnu.version_r and .gnu.version_d to 4 byte boundaries
They don't contain 8 byte date items, so they don't need to be aligned
to 8 byte boundaries.
2024-10-11 19:51:52 +09:00
Rui Ueyama fdaa13c374 Merge .gnu.build.attributes sections into a single section
It looks like gold does this.
2024-10-11 19:42:14 +09:00
Rui Ueyama fa2411cc08 Refactor 2024-10-10 10:14:36 +09:00
Rui Ueyama a69b332f86 Enable GC-sections and ICF not only for Release but also for RelWithDebInfo 2024-10-09 21:11:55 +09:00
Rui Ueyama 20dccf2b77 Refactor 2024-10-09 15:59:15 +09:00
Rui Ueyama ffd10dd447 Do not include mimalloc-new-delete.h if mimalloc is disabled
Including mimalloc-new-delete.h overrides new and delete operators
with mimalloc. If a user choose not to use mimalloc, we shouldn't
include that file.

https://github.com/rui314/mold/issues/1355
2024-10-09 14:41:36 +09:00
Rui Ueyama ce9cdb1bae [ICF] Handle RELRO sections correctly
When control reaches here, `output_section` is always a nullptr.
2024-10-08 19:35:03 +09:00
Rui Ueyama 979084b593 Use .llvm_addrsig to find address significance for executable sections
This change should allow more executable sections to be merged by ICF.
2024-10-08 17:47:41 +09:00
Rui Ueyama c562c733ce Move target-dependent data members to the end of class definitions
So that the data layouts for other data members become the same.
This makes ICF work better.
2024-10-08 17:45:25 +09:00
Rui Ueyama 3c6f57cc27 Refactor 2024-10-08 14:57:32 +09:00
Rui Ueyama c16d6e8cc0 Remove to_abs_path
This function is not necessary.

I don't know how it could leak memory, but it was reported
that it does in https://github.com/rui314/mold/issues/1355.
2024-10-08 13:01:12 +09:00
Rui Ueyama 91e2daf3bf Create release binaries with Clang instead of GCC
LLVM/Clang works better with mold's --icf=safe because of the presence
of LLVM address significance table.
2024-10-07 19:29:01 +09:00
Rui Ueyama 7204c6dec8 Do not set a value to relr.dyn section's sh_link
The ELF ABI doesn't say anything about sh_link field of SHT_RELR section,
so setting a value to it is simply wrong.
2024-10-06 15:28:36 +09:00
Rui Ueyama 3441cbb636 Enable GC sections and ICF for binary distributions to reduce file size 2024-10-06 09:49:32 +09:00
Rui Ueyama 3070e798f3 Refactor 2024-10-05 20:05:43 +09:00
Rui Ueyama 754354991b Simplify 2024-10-05 15:37:03 +09:00
Rui Ueyama 86fe020558 Fix CI 2024-10-05 15:13:46 +09:00
Rui Ueyama 40e7b983d6 Fix CI 2024-10-05 14:44:22 +09:00
Rui Ueyama f69ecd616d Merge pull request #1353 from praetorian20/main 2024-10-05 13:03:17 +09:00
Rui Ueyama d683f62e01 Add a rule for KDE Neon
Fixes https://github.com/rui314/mold/issues/1354
2024-10-05 12:44:20 +09:00
Ashish Sadanandan 205a091924 Use readelf for compress-debug-sections verification
RHEL 8.7's version of dwarfdump (2018-01-29) is too old to recognize the
SHF_COMPRESSED flag in debug info, so the compress-debug-sections test
fails despite the debug sections being compressed as intended.

To fix this, we will use readelf to dump information about the
executable. The COMPRESSED flag can be verified using this.
2024-10-04 20:44:05 -06:00
Rui Ueyama 7b4d2e77ee Refactor 2024-10-05 10:08:23 +09:00
Rui Ueyama 2c9ba071bc Refactor 2024-10-04 20:47:50 +09:00
Rui Ueyama de65f6bd75 Bump mold version to 2.34.1 2024-10-04 15:00:22 +09:00
Rui Ueyama 186272a425 Handle R_ARM_TARGET1 as a synonym for R_ARM_ABS32
This fixes a regression introduced in 431fc39db0.

Fixes https://github.com/rui314/mold/issues/1351
2024-10-04 14:18:13 +09:00
Rui Ueyama 5fcbf25260 Refactor 2024-10-03 15:19:47 +09:00
Rui Ueyama 16eb5139f1 [RISC-V] Set DT_RISCV_VARIANT_CC if there's a dynamic symbol with STO_RISCV_VARIANT_CC 2024-10-02 20:41:13 +09:00
Rui Ueyama 35be7ba887 Refactor 2024-10-02 20:39:50 +09:00
Rui Ueyama 55f0289a41 Non-SHF_ALLOC sections' addresses should be displayed as zero for --Map 2024-09-30 13:28:22 +09:00
Rui Ueyama 36dd6babe1 Refactor 2024-09-29 19:04:22 +09:00
Rui Ueyama 91b6818b62 Revert "Refactor"
This reverts commit 9e9539144e because
the new code didn't compile on macOS.
2024-09-27 21:45:49 +09:00
Rui Ueyama 9e9539144e Refactor 2024-09-27 17:51:54 +09:00
Rui Ueyama feb9b3eb4e Set 0 to DT_AARCH64_VARIANT_PCS
It looks like only the presence of the entry is important, but by
convention, GNU ld and lld set 0 to a DT_AARCH64_VARIANT_PCS dynamic
entry, so we should follow suit.
2024-09-27 17:51:54 +09:00
Rui Ueyama 4b3fc4e82a Refactor 2024-09-27 17:51:54 +09:00
Rui Ueyama ed7cc1b85a Bump mold version to 2.34.0 2024-09-25 10:01:39 +09:00
Rui Ueyama 3711ddb95e Drop DEC Alpha processor support
Keeping DEC Alpha support is difficult because there are no Alpha
machines available for toolchain developers, and even if there were,
no modern Linux distros still support Alpha. For example, Debian
dropped Alpha support in Debian 6.0. As a result, I cannot test my
linker with real-world programs.

In fact, I believe mold has never been able to link real-world large
programs. Object files for Alpha are compiled with the small code
model by default, so it is not easy to support it in the linker.

There's no means to verify that my implementation is correct for Alpha
because Alpha's psABI has never been published by DEC. The most
"reliable" source of correctness is GNU ld's source code and comments.

Last but not least, I believe there are literally zero mold/Alpha
users. Unlike some other retro computers like the m68k, Alpha doesn't
seem to get much love from the community, perhaps because the
availability of Alpha machines was limited in the first place, even in
the 90s.

Therefore, I'll remove Alpha support now. If there's someone who wants
to keep it, we can resurrect and re-test it.

This change should make the mold binary size a little smaller.
2024-09-24 17:29:34 +09:00
Rui Ueyama 0ee12e4115 Do not strip linker-synthesized symbols when --retain-symbols-file is given
This is an attempt to fix https://github.com/rui314/mold/issues/1346
2024-09-21 17:08:32 +09:00
Rui Ueyama 87c9929df9 Merge pull request #1345 from andreas-schwab/main 2024-09-19 17:32:46 +09:00
Andreas Schwab f5f14d3faa test/absolute-symbols.sh: move symbol address out of the heap
Fixes #1340

Signed-off-by: Andreas Schwab <[email protected]>
2024-09-19 09:53:41 +02:00
Rui Ueyama 5be5ea9ff5 Fix CI 2024-09-18 15:18:44 +09:00
Rui Ueyama f1739bffd0 Refactor 2024-09-18 15:03:54 +09:00
Rui Ueyama 1efbe3f141 Do not lower the priority of DSO symbols than in-archive symbols
This commit reverts 0612ea41f1 and
instead solves the original issue with a different approach.

Now, if a hidden symbol was resolved to a DSO symbol, we'll redo
symbol resolution so that the symbol won't be resolved to the same one
next time.

This should fix our build-all buildbot.
2024-09-17 13:23:17 +09:00
Rui Ueyama 5ba1c36fbb Refactor 2024-09-17 12:27:08 +09:00
Rui Ueyama c217134d97 Refactor 2024-09-17 11:58:42 +09:00
Rui Ueyama fea866d910 Refactor 2024-09-15 19:18:06 +09:00
Rui Ueyama 3b3bc7b41e Make builders runnable by hand 2024-09-15 19:13:45 +09:00
Rui Ueyama 0612ea41f1 Lower the priority of DSO symbols than in-archive symbols
Assume both `foo.a` and `bar.so` define the same symbol `baz`.
If `baz`'s symbol visibility is hidden, it needs to be resolved within
the output file, i.e., from `foo.a`. However, previously, such symbol
was resolved to the one in `bar.so`.

To fix the problem, we'll lower the symbol priority for DSOs.

Fixes https://github.com/rui314/mold/issues/1342
2024-09-15 17:17:43 +09:00
Rui Ueyama 940ac8125c Refactor 2024-09-15 16:22:18 +09:00
Rui Ueyama 583ba8ade1 Fix daily builder 2024-09-15 11:13:56 +09:00
Rui Ueyama 82feeed693 Build mold binary with mold 2024-09-14 19:11:51 +09:00
Rui Ueyama e7e41329ef Refactor 2024-09-14 17:14:30 +09:00
Rui Ueyama 63d990e49a Upgrade upload-artifact github action 2024-09-14 16:00:17 +09:00
Rui Ueyama 8d5b753337 Fix buildbot 2024-09-14 15:10:50 +09:00
Rui Ueyama 431fc39db0 Change how we handle word-size absolute relocations
Word-size absolute relocations, such as R_X86_64_64, are special from
the linker's point of view because only such symbols can be promoted
to dynamic symbols. This patch handles them separately.
2024-09-14 11:59:20 +09:00
Rui Ueyama 7734a93217 Improve a test 2024-09-14 08:04:15 +09:00
Rui Ueyama 806d7409da Refactor 2024-09-07 19:47:18 +09:00
Rui Ueyama 2e326cc006 Refactor 2024-09-07 15:31:56 +09:00
Rui Ueyama e1ace5ea00 Add a FreeBSD test runner 2024-09-02 14:58:30 +09:00
Rui Ueyama 8e010c2095 Update comments 2024-09-01 13:20:50 +09:00
Rui Ueyama 6758b53bff Fix CI 2024-08-31 15:14:26 +09:00
Rui Ueyama f6822fb0f3 Make executables iwth zero-size segments loadable on FreeBSD
Fixes https://github.com/rui314/mold/issues/1338
2024-08-31 14:52:11 +09:00
Rui Ueyama f0d39b2460 Add a rule for FreeBSD 2024-08-31 13:26:52 +09:00
Rui Ueyama a3e69502b0 Refactor 2024-08-30 12:04:56 +09:00
Rui Ueyama 4645248106 Merge pull request #1337 from leleliu008/fix-netbsd-no-alloca.h 2024-08-30 04:58:24 +09:00
leleliu008 80648584cf alloca.h is missing on NetBSD
alloca(3) is defined in stdlib.h on NetBSD

Reference: https://man.netbsd.org/alloca.3

Signed-off-by: leleliu008 <[email protected]>
2024-08-29 20:22:03 +08:00
Rui Ueyama d4c2073cda Fix test
Previous test passed even without 1c321022d0.
2024-08-27 20:09:40 +09:00
Rui Ueyama fe7237e454 Merge pull request #1335 from ywgrit/main 2024-08-27 17:04:46 +09:00
Xin Wang 4da257e6fb LoongArch: change test of R_LARCH_CALL36 with bigger offset 2024-08-27 15:14:56 +08:00
Rui Ueyama ed617fef05 Merge pull request #1170 from mominul/patch-1
Update README.md
2024-08-27 13:19:16 +09:00
Xin Wang 1c321022d0 LoongArch: fix error offset for relaxation of R_LARCH_CALL36 2024-08-26 16:37:09 +08:00
Rui Ueyama ed4cae9354 Add a test 2024-08-24 14:33:13 +09:00
Rui Ueyama 2d390565c1 Fix the section-shrinking relaxation 2024-08-24 13:53:02 +09:00
Rui Ueyama 5e72d88940 Update a document 2024-08-24 13:21:38 +09:00
Rui Ueyama 213f2ecec5 Refactor 2024-08-23 13:45:02 +09:00
Rui Ueyama 23b3778383 Refactor 2024-08-22 18:25:28 +09:00
Rui Ueyama b9776af9b2 Refactor 2024-08-22 17:34:42 +09:00
Rui Ueyama 25794c6003 Refactor 2024-08-22 11:09:55 +09:00
Rui Ueyama 7fa561699e Refactor 2024-08-21 13:57:26 +09:00
Rui Ueyama 127ba25dab [LoongArch] Fix TLSDESC relaxation
R_LARCH_TLS_DESC_PC_HI20 is a GOT-loading relocation, so we need to
compute the distance to the symbol's GOT entry instead of to the
symbol's address.
2024-08-21 13:25:59 +09:00
Rui Ueyama 6a1742ea90 Refacotr 2024-08-21 12:53:16 +09:00
Rui Ueyama e013d27b54 Add a test 2024-08-21 12:40:56 +09:00
Rui Ueyama 37e175b4a4 Merge pull request #1333 from ywgrit/main
LoongArch: apply relaxation to tls desc
2024-08-21 12:12:15 +09:00
Xin Wang 982b901077 LoongArch: apply relaxation to tls desc 2024-08-21 09:44:43 +08:00
Rui Ueyama f0797bb8f2 Improve CI 2024-08-18 14:22:57 +09:00
Rui Ueyama 18f8c425dc Do not use precompiled headers
`-fpch-preprocess` doesn't seem to improve build speed that much,
so don't use it.
2024-08-18 12:56:38 +09:00
Rui Ueyama 194239f8c6 Enable some tests 2024-08-18 12:39:29 +09:00
Rui Ueyama 401e019b05 Add a test
We also don't want to skip empty string because if a user specifies
an empty string, it is their intention whatever that intention is.
2024-08-18 11:38:47 +09:00
cristian64 0c74e8274e Fix infinite loop in split_by_comma_or_colon(). (#1332)
* Fix infinite loop in `split_by_comma_or_colon()`.

The mere presence of a `,` or a `:` in the input string would make the
function enter an infinite loop, effectively freezing the program until
all available memory was exhausted. The string position in the loop was
not incremented past the `,` or the `;` character that had just been
read.

The function is currently used only in the `--exclude-libs` option. The
freeze is reproducible with:

```bash
$ mold --exclude-libs foo,bar
```

The longer the string after the first `,` or `:` is, the faster memory
exhaustion is reached.

* Skip empty parts in `split_by_comma_or_colon()`.
2024-08-18 11:34:12 +09:00
Rui Ueyama cf5d054eb4 Fix CI 2024-08-17 16:17:31 +09:00
Rui Ueyama d9bf693fb8 [LoongArch] Do not emit local symbols by default 2024-08-17 15:09:08 +09:00
Rui Ueyama cf38f81c39 Refactor 2024-08-17 15:09:08 +09:00
Rui Ueyama 2eeb7b411e Simplify 2024-08-17 06:38:31 +09:00
Rui Ueyama ecc7ce022b Skip ELF symbols whose section numbers are the reserved range
Fixes https://github.com/rui314/mold/issues/1331
2024-08-16 13:26:12 +09:00
Rui Ueyama a6b182b065 Refactor 2024-08-16 13:26:12 +09:00
Rui Ueyama e30f01fe93 Refactor 2024-08-16 12:48:16 +09:00
Rui Ueyama ac84d2cdbb Refactor 2024-08-16 11:18:12 +09:00
Rui Ueyama 742ea87d41 Merge .ARM.exidx entries
This change should make our output for ARM32 a little bit smaller.
2024-08-15 17:15:45 +09:00
Rui Ueyama fa546965ec Refactor 2024-08-15 14:11:55 +09:00
Rui Ueyama f68657d201 Refactor 2024-08-15 13:34:41 +09:00
Rui Ueyama 23bc92c66f Emit PT_GNU_PROPERTY program header if .note.gnu.property is present
It looks like Linux kernel reads this program header to enable BTI on ARM64.
2024-08-15 12:57:45 +09:00
Rui Ueyama c2956484ca Refactor 2024-08-15 12:26:29 +09:00
Rui Ueyama 3f593c62ae Remove code for macOS 2024-08-14 21:29:08 +09:00
Rui Ueyama 5b8594979b Reorganize directory structure 2024-08-14 20:52:09 +09:00
Rui Ueyama 7571305cd1 Fix CI 2024-08-14 20:06:28 +09:00
Rui Ueyama 0631f9750e Fix a Docker image build failure
Fixes https://github.com/rui314/mold/issues/1330
2024-08-14 19:10:40 +09:00
Rui Ueyama b51d6ab5cb Avoid LD_PRELOAD 2024-08-14 19:10:40 +09:00
Rui Ueyama 93c4f35615 Fix minor issues 2024-08-14 17:38:53 +09:00
Rui Ueyama 510b2d68c0 Reorganize directory structure 2024-08-14 16:23:25 +09:00
Rui Ueyama d6d8d2e494 [LoongArch] Fix symbol value computation for the extreme code model
The LoongArch extreme code model uses the following instructions to
materialize a 64-bit value in a regiter.

  pcalau12i $t1, %pc_hi20(g2)
  addi.d    $t0, $zero, %pc_lo12(g2)
  lu32i.d   $t0, %pc64_lo20(g2)
  lu52i.d   $t0, $t0, %pc64_hi12(g2)

The previous relocation formula expected that `page(pc)` would be
consistent for all the instructions, but this assumption is incorrect
if the instruction sequence crosses a 4 KiB boundary.

Now, the LoongArch psABI requires that the machine instructions must be
consecutive, and the relocations for lu32.d and lu52i.d uses
`page(pc - 8)` and `page(pc - 16)` instead of `page(pc)`.

This psABI change gave me an impression that the LoongArch's extreme code
model was poorly designed and inadequately tested. If these instructions
must be consecutive, only a single relocation referring to the beginning
of the instructions would suffice, which relocates all the following four
instructions at once.

Maybe we do not need to support the relocations for the extreme code model
because the code model was buggy at the spec level, which suggests that no
one is using them. But I'm not sure if it is safe to remove them, so let's
just follow the psABI change.
2024-08-14 13:52:12 +09:00
Rui Ueyama 9e0c1c81dd Refactor 2024-08-13 20:52:38 +09:00
Rui Ueyama ac5031198c Simplify 2024-08-13 20:18:50 +09:00
Rui Ueyama 11d5426781 Update mold.1 (automated commit) 2024-08-13 09:22:48 +00:00
Rui Ueyama afc52eea70 Accept --build-id=fast for the sake of compatibility with lld
Fixes https://github.com/rui314/mold/issues/1328
2024-08-13 18:20:56 +09:00
Rui Ueyama 6248d964fb Refactor 2024-08-13 18:15:20 +09:00
Rui Ueyama addb5f1675 [SPARC] Relax TLS GD/LD relocations 2024-08-13 17:18:01 +09:00
Rui Ueyama ed22d76ef0 Refactor 2024-08-13 14:46:53 +09:00
Rui Ueyama a73e9b2dac Update comments 2024-08-13 11:27:45 +09:00
Rui Ueyama 5dfa1cf07c Update comments 2024-08-10 09:51:13 +09:00
Rui Ueyama 4164285a13 Fix CI 2024-08-09 22:10:53 +09:00
Rui Ueyama 1f53bd5bfa Attempt to fix CI 2024-08-09 18:22:07 +09:00
Rui Ueyama a32d294df8 Update comments 2024-08-09 18:02:14 +09:00
Rui Ueyama dbaa6d7341 [LoongArch] Support TLSDESC 2024-08-09 17:49:12 +09:00
Rui Ueyama 23a9e7261d Refactor 2024-08-09 15:04:04 +09:00
Rui Ueyama 5a9919b433 Fix a crash bug
Fixes https://github.com/rui314/mold/issues/1326
2024-08-09 12:38:56 +09:00
Rui Ueyama 1d24b70996 Use crc32() instead of crc32_z()
Old zlib does not support crc32_z. The difference between the two is
that crc32() takes an input size as an unsigned int while crc32_z()
takes as size_t. For our use case, size is guranteed to be short, so
crc32() is safe to use.
2024-08-08 20:06:19 +09:00
Rui Ueyama f18d1c1ffc Attempt to fix build failure on 32 bit Alpine distros
Fixes https://github.com/rui314/mold/issues/1323
2024-08-08 10:09:41 +09:00
Rui Ueyama 1ab637cea4 Bump mold version to 2.33.0 2024-08-07 12:32:13 +09:00
Rui Ueyama 573b8a45df Update mold.1 (automated commit) 2024-08-06 05:26:45 +00:00
Rui Ueyama 0bf00a4247 Remove --encoded-package-metadata from docs
It's not clear whether or not the option would be added to the GNU linker,
so I"ll make this a hidden option for now.

https://github.com/rui314/mold/pull/1308
2024-08-06 14:25:45 +09:00
Rui Ueyama 43d9536a05 Fix an assertion failure
https://github.com/rui314/mold/issues/573
2024-08-05 20:06:45 +09:00
Rui Ueyama 893ec1b362 Format 2024-08-05 15:08:57 +09:00
Rui Ueyama 1aa966c2ad Refactor 2024-08-05 13:26:03 +09:00
ywgrit 121f917884 LoongArch: support relaxation of pcalau12i/ld.d to pcalau12i/addi.d or pcaddi (#1322)
[LoongArch] support relaxation of pcalau12i/ld.d to pcalau12i/addi.d
2024-08-05 12:26:03 +09:00
Rui Ueyama 17a9481522 Merge .sdata, .srodata and .sbss
RISC-V uses these sections for small data/rodata/bss.
2024-08-03 14:58:27 +09:00
Rui Ueyama 06e8bf8933 Update mold.1 (automated commit) 2024-08-03 03:15:37 +00:00
Rui Ueyama f7af61f80c Update man page 2024-08-03 12:14:59 +09:00
Rui Ueyama 25b34bb472 Update mold.1 (automated commit) 2024-08-02 11:02:16 +00:00
Rui Ueyama 22f85c1b6f Rename --remove-landing-pad -z rewrite-endbr 2024-08-02 19:18:03 +09:00
Rui Ueyama dd8d9714cb dSupport --dynamic-list-data
Fixes https://github.com/rui314/mold/issues/1320
2024-08-01 18:51:34 +09:00
Rui Ueyama 982f07168d Refactor 2024-08-01 11:51:28 +09:00
Rui Ueyama 218d89b005 Rename a test 2024-07-31 21:00:12 +09:00
Rui Ueyama bada67bb39 Handle R_LARCH_CALL36 as a function-calling relocation 2024-07-31 20:58:45 +09:00
Rui Ueyama db03248e72 Fix comments 2024-07-31 20:05:52 +09:00
Rui Ueyama 47c092aac0 Remove range extension support from LoongArch
It looks like emitting PCADDU18I + JIRL instead of just BL for function
calls is a way to go, so we don't want to bother to maintain range
extension thunks for the psABI.

Since no other linkers support range extension thunks for LoongArch other
than us, removing the support should be OK.
2024-07-31 19:23:03 +09:00
Rui Ueyama 4e6b4c43cd Refactor 2024-07-31 19:09:56 +09:00
Rui Ueyama 2c6f379d69 Support R_LARCH_CALL36 2024-07-31 17:51:34 +09:00
Rui Ueyama bf79ae1190 Refactor 2024-07-31 14:55:05 +09:00
Rui Ueyama 96fda95624 Add a test for LoongArch relaxation 2024-07-31 11:48:04 +09:00
Rui Ueyama b71bfccb46 Improve pcalau12i+addi.d relaxation
This should unbreak our buildbots.
2024-07-31 11:36:32 +09:00
Rui Ueyama a4dc015c0d Update comments 2024-07-31 10:39:22 +09:00
ywgrit 74b359fe62 [LoongArch] Support relax of pcalau12i/addi.d->pcaddi (#1318)
Relax pcalau12i/addi.d to pcaddi
2024-07-31 10:39:06 +09:00
Rui Ueyama 197a74ded6 Add comments 2024-07-29 16:03:35 +09:00
Rui Ueyama 9aa4fcc6cd Show skipped tests
I don't know why, but the previous regular expression didn't match with
a line consisting just with "skipped".
2024-07-29 13:41:34 +09:00
Rui Ueyama 36915416dd Run a binary under Intel SDE CPU emulator with CET enabled 2024-07-29 13:16:12 +09:00
Rui Ueyama 411dcfd0b1 Fix comments 2024-07-29 13:13:54 +09:00
Rui Ueyama 35564d5f2c Update mold.1 (automated commit) 2024-07-29 02:00:32 +00:00
Rui Ueyama bba20b3fe7 Fix typos 2024-07-29 10:59:12 +09:00
Rui Ueyama 55e516da6f Update mold.1 (automated commit) 2024-07-29 01:55:03 +00:00
Rui Ueyama 2c3dcc4370 Document --remove-landing-pads 2024-07-29 10:52:31 +09:00
Rui Ueyama 1fb6dd3b46 Rename -z rewrite-endbr --remove-landing-pads
This is because we want to support it for non-x86-64 targets.
Using the x86 instruction name as a parameter name is not desirable.
2024-07-29 10:52:21 +09:00
Rui Ueyama ed7eec5969 Fix -z rewrite-endbr
Previously, functions that are referred to only by section symbols were
not considered address-taken, and we always rewrote endbr64s with NOPs for
such functions. That caused a runtime fault when such function was called
indirectly.

Now, endbr64s for such symbols are retained.

I tried to build clang-19 as a release build with and without `-z
rewrite-endbr` and counted the number of endbr64 instructions in each
binary. Here is the result:

  Before: 110,615
  After:   91,799

So the feature reduces the number of gadgets by 17%. Both binaries worked
fine with Intel SDE CPU emulator with `-cet 1 -cet_raise 1`, so I think
it's finally working as expected.

I also tried to build mold itself with and without the feature. Here is
the result:

  Before: 27,430
  After:  17,725

This is a 35% reduction. I confirmed that mold built with `-z rewrite-endbr`
can self-host under Intel SDE.

The rewrite_endbr pass is extremely fast. It took only 7 milliseconds
for a ~210 MiB clang-19 binary on my Threadripper 7990X machine. We
may want to consider enabling it by default at some point.
2024-07-29 09:06:26 +09:00
Rui Ueyama 3f7236d268 Refactor 2024-07-28 19:42:05 +09:00
Rui Ueyama 766ce80297 Update comments 2024-07-28 17:01:13 +09:00
Rui Ueyama 61af6f3c59 Report an error for an enormously large r_addend 2024-07-27 16:14:20 +09:00
Rui Ueyama 464f7655d6 Use break instead of nop as a filler 2024-07-27 14:50:38 +09:00
Rui Ueyama d1674d86ac Add comments 2024-07-27 14:50:38 +09:00
Rui Ueyama 36e5b4b949 Relax R_LARCH_TLS_LE_{HI20_R,ADD_R,LO12_R} 2024-07-27 11:03:45 +09:00
Rui Ueyama 98a7cff0c5 Handle R_LARCH_RELAX 2024-07-27 11:03:45 +09:00
Rui Ueyama 34ed39c873 Refactor 2024-07-27 11:03:43 +09:00
Rui Ueyama 2bbba15068 Create canonical PLTs for PIEs
We cannot guarantee pointer equality for some corner cases without them.
2024-07-24 10:56:39 +09:00
Rui Ueyama a478db77c2 Do not obtain an unnecessary lock 2024-07-22 12:05:19 +09:00
Rui Ueyama d829e425df Add a missing #include
https://github.com/microsoft/mimalloc/pull/917
2024-07-22 11:06:37 +09:00
Rui Ueyama f5b2b82295 Skip a test if LD_PRELOAD doesn't work
May be related to the following issues:

  https://github.com/rui314/mold/issues/1305
  https://github.com/rui314/mold/issues/1309
2024-07-22 10:39:32 +09:00
Rui Ueyama e3876ddbd4 Refactor 2024-07-22 08:47:25 +09:00
Rui Ueyama b8556e37bb Fix CI 2024-07-21 20:18:31 +09:00
Rui Ueyama bbfaa0bb30 Fix CI 2024-07-21 19:47:12 +09:00
Rui Ueyama 5606087776 Support -z x86-64-v[234] 2024-07-21 18:51:28 +09:00
Rui Ueyama b919548b27 Refactor 2024-07-21 16:42:39 +09:00
Rui Ueyama ed7414e1c7 Update mold.1 (automated commit) 2024-07-21 06:16:56 +00:00
Rui Ueyama fa1237fc08 Document --no-allow-shlib-undefined 2024-07-21 15:14:48 +09:00
Rui Ueyama 1dc99aaade Fix CI 2024-07-21 13:26:58 +09:00
Rui Ueyama 3001f02b55 Support --no-allow-shlib-undefined
Fixes https://github.com/rui314/mold/issues/1313
2024-07-21 13:06:57 +09:00
Rui Ueyama 39442dbdf0 Refactor 2024-07-20 16:05:37 +09:00
Rui Ueyama 6c4ca4e40c Attempt to fix CI 2024-07-20 15:06:15 +09:00
Rui Ueyama bdca85e15a Make Integer's constructor constexpr 2024-07-20 14:54:58 +09:00
Rui Ueyama 1ccc77ac7f Refactor 2024-07-20 13:38:03 +09:00
Rui Ueyama b99ed6e7ae Refactor 2024-07-20 13:10:33 +09:00
Rui Ueyama 6ee6410948 Build and test on more Linux distros 2024-07-19 18:03:57 +09:00
Rui Ueyama d187dac602 Install binary packages in Gentoo 2024-07-19 18:03:57 +09:00
Rui Ueyama 2b4c365161 Run tests on Alpine Linux with musl libc 2024-07-19 15:46:15 +09:00
Rui Ueyama 70ea7ade7f Do not use the extended regexp
It looks like Busybox's grep doesn't support regexp backreferences.

Fixes https://github.com/rui314/mold/issues/1309
2024-07-19 15:23:52 +09:00
Rui Ueyama 56a94d2693 Fix a test on musl
Fixes https://github.com/rui314/mold/issues/1309
2024-07-19 14:54:33 +09:00
Rui Ueyama 14a4b05950 Always create a .eh_frame section even if it's empty
glibc's `__register_frame_info_bases` expect that it can read a terminator
if a .eh_frame is empty. Removing the section completely caused a crash bug
on Alpine Linux with musl as libc.

Specifically, mold-generated binaries crash if all the following
condition were met:

 1. musl is used as a libc
 2. .eh_frame is empty
 3. libgcc_s.so is linked

Prior to 06b592683c, condition (3) was not
usually satisfied, so we didn't notice this issue. Now, the shared library
is linked against almost all output files because the file is referenced by
`__deregister_frame_info`.

https://github.com/bminor/glibc/blob/910aae6e5a2196938fc30fa54dd1e96f16774ce7/sysdeps/generic/unwind-dw2-fde.c#L105-L107

Fixes https://github.com/rui314/mold/issues/1309
2024-07-19 14:51:00 +09:00
Rui Ueyama 14d525c1a1 Refactor 2024-07-18 21:04:55 +09:00
Rui Ueyama 5b7ae06bde Update mold.1 (automated commit) 2024-07-18 06:53:23 +00:00
Rui Ueyama e67f5e62ae Fix update-manpage workflow 2024-07-18 15:51:56 +09:00
Rui Ueyama bae9598190 Refactor 2024-07-18 13:54:16 +09:00
Rui Ueyama 9acda6f939 Make update-manpage workflow runnable by hand 2024-07-18 13:06:08 +09:00
Rui Ueyama 7b52f34c38 Make test scripts more verbose 2024-07-18 12:57:32 +09:00
Rui Ueyama 9874a9daa7 Document --encoded-package-metadata
https://github.com/rui314/mold/pull/1308
2024-07-18 11:57:48 +09:00
Rui Ueyama aecf39d459 Do not crash when an empty argument is given 2024-07-18 11:57:48 +09:00
Rui Ueyama 9eacc638f3 Improve a test 2024-07-18 11:57:48 +09:00
Rui Ueyama 0c34ec1f50 Refactor 2024-07-18 09:39:01 +09:00
Rui Ueyama a778f9c5f4 Merge pull request #1308 from bdrung/encoded-package-metadata
Add --encoded-package-metadata
2024-07-17 22:23:44 +09:00
Rui Ueyama d7b079c4fb Refactor 2024-07-17 21:26:36 +09:00
Benjamin Drung cfe60991a5 Add --encoded-package-metadata
Specifying the compiler flag `-Wl,--package-metadata=<JSON>` might not
work, because the shells might eat the quotation marks and the compiler
splits the JSON at the commas.

Ubuntu tried to using a specs file to set `--package-metadata` but that
turned out to be too fragile. autopkgtests might use the compiler flags
but the needed environment variables are not set in the test
environment. Debugging a crash of an application build with the -spec
parameter lacks the environment variables. People like to iteratively
continue building the software in the build directory while hacking on
the package and then have no environment variable set.

So introduce a `--encoded-package-metadata` linker flag that takes a
percent-encoded JSON. Percent-encoding is used because it is a
standard and simple to implement.

Bug-Ubutru: https://bugs.launchpad.net/bugs/2071468
Signed-off-by: Benjamin Drung <[email protected]>
2024-07-17 14:14:48 +02:00
Rui Ueyama 2acf634340 Fix typo 2024-07-17 18:50:48 +09:00
Rui Ueyama 09f1008186 Handle R_LARCH_TLS_LE_HI20_R and R_LARCH_TLS_LE_LO12_R 2024-07-17 18:36:12 +09:00
Rui Ueyama baf9ae9038 Fix a test on Debian
If the default linker doesn't complain, just skip the test.

Fixes https://github.com/rui314/mold/issues/1301
2024-07-16 12:00:08 +09:00
Rui Ueyama cdcb428f26 Remove dead code 2024-07-15 13:41:57 +09:00
Rui Ueyama 36bc198b9a Refactor 2024-07-15 13:36:07 +09:00
Rui Ueyama 428a9c4228 Skip a test on m68k
https://github.com/rui314/mold/pull/1304
2024-07-15 11:19:06 +09:00
Rui Ueyama 666b18b851 Use __attribute__((aligned)) instead of a C macro 2024-07-15 11:06:11 +09:00
Rui Ueyama f180ac21c8 Fix build on non-m68k hosts
https://github.com/rui314/mold/pull/1304
2024-07-14 21:49:58 +09:00
Rui Ueyama 0b1be96a13 Merge pull request #1304 from chewi/m68k-alignment
Align Chuck, InputSection, and SectionFragment to at least 4 bytes
2024-07-14 21:44:35 +09:00
James Le Cuirot 1f444a21d2 Align Chuck, InputSection, and SectionFragment to at least 4 bytes
There are already static_asserts to check this, so it is almost always
true anyway, but not on m68k, which aligns to 2 bytes by default.
2024-07-14 10:22:04 +01:00
Rui Ueyama 4eeebf0221 Define Script class and move linker script-related code there 2024-07-14 16:41:03 +09:00
Rui Ueyama b2a1e4b83e Move reader-related attributes to ReaderContext 2024-07-14 11:10:59 +09:00
Rui Ueyama 08938402c9 Rename a variable 2024-07-13 18:19:11 +09:00
Rui Ueyama 4c569c8c8c Accept --library as a synonym for -l
No one has complained about it so far, but according to the GNU ld manual,
`--library` is a synonym for `-l`.
2024-07-13 18:18:35 +09:00
Rui Ueyama 03a2105d39 Change how we resolve relocations referring to mergeable sections
Previously, we find a string in a mergeable string section by offset
after adding a relocation addend to a symbol value. Now, we find a
string just by a symbol value and then add a relocation addend later.

Assume a mergeable section containing two strings as an example.

  foo\0bar\0
  ^    ^
  .L1  .L2

Previously, a relocation referring to .L2 - 1 referred to the end of
.L1. Now, it refers to one byte before .L2.

Section symbol is an exception to this rule, because it seems it's
what the compiler is assuming. The rules seem arbitrary, but it looks
like we need them for the compatibility with existing object files.

Fixes https://github.com/rui314/mold/issues/1300
2024-07-13 17:25:44 +09:00
Rui Ueyama d2870ac574 Upgrade bundled mimalloc to 2.1.7 2024-07-11 15:38:18 +09:00
Rui Ueyama 8497b7e1dc Upgrade OneTBB in ./third-party to version 2021.13.0 2024-07-11 15:38:18 +09:00
Rui Ueyama 0e2b6b010d Update mold.1 (automated commit) 2024-07-11 04:39:27 +00:00
Rui Ueyama 84263109b1 Revert "Allow numbers other than 1 for MOLD_JOBS"
This reverts commit 863893057e because
it's unstable.
2024-07-11 13:36:55 +09:00
Rui Ueyama 162bcca5df Update mold.1 (automated commit) 2024-07-11 02:10:41 +00:00
Rui Ueyama 2fb3c9da57 Update man page 2024-07-11 11:09:03 +09:00
Rui Ueyama 83ee4962b1 Fix test flakiness for SH4 2024-07-11 09:58:16 +09:00
Rui Ueyama e03f74a357 Fix CI
Fixes https://github.com/rui314/mold/issues/1299
2024-07-10 15:52:46 +09:00
Rui Ueyama 8c1b25b3aa Obtain a file lock with flock() while creating a separate debug file 2024-07-09 17:49:39 +09:00
Rui Ueyama 315dfd3244 Fix CI 2024-07-09 15:25:53 +09:00
Rui Ueyama 02ca830f42 Refactor 2024-07-09 15:07:41 +09:00
Rui Ueyama 18da5b654e Add --no-detach to write to a separate debug file in the foreground
--detach is the default.
2024-07-09 12:06:41 +09:00
Rui Ueyama 97a1e218c5 Simplify crc32_solve()
The code was originally written by Pete Cawley
https://gist.github.com/corsix/bdfc8f2f1dc0f28de39f74de9bf4f060
2024-07-09 10:15:26 +09:00
Rui Ueyama f9e4cb1a7f Add a missing #include 2024-07-08 10:43:40 +09:00
Rui Ueyama 60760a892a Attempt to fix CI 2024-07-08 09:59:07 +09:00
Rui Ueyama 596ffa959a Add --separate-debug-info
This option is to separate debug info to a different file. The debug
info file's filename is stored to the main output file's .gnu_debuglink
section. gdb can read the section contents and followg the link to
find debug info in another file.

Fixes https://github.com/rui314/mold/issues/1294
2024-07-08 09:28:32 +09:00
Rui Ueyama cd3b817f13 Make notify_parent idempotent 2024-07-07 22:49:35 +09:00
Rui Ueyama 19de40fed4 Refactor 2024-07-07 22:45:17 +09:00
Rui Ueyama e78e12b15b Refactor 2024-07-07 22:24:42 +09:00
Rui Ueyama 5b4377842b Fix CI 2024-07-07 21:50:53 +09:00
Rui Ueyama 16ee9f48a4 Fix CI 2024-07-07 17:58:21 +09:00
Rui Ueyama 8e3679e4e5 Refactor 2024-07-06 13:11:09 +09:00
Rui Ueyama 7089304046 Simplify 2024-07-05 19:47:33 +09:00
Rui Ueyama 5ab3708e9f Merge pull request #1298 from yujincheng08/patch-1
Fix cross-compile lto on MacOS
2024-07-05 19:45:10 +09:00
Rui Ueyama aa939d7a6a Temporarily disable ASAN tests
It looks like starting with Clang 18.1.3, ASAN began reporting an
error on OneTBB.
2024-07-05 18:27:00 +09:00
Rui Ueyama a63bbcd7b1 Attempt to fix CI 2024-07-05 17:58:13 +09:00
Rui Ueyama 339f9c485a Attempt to fix CI 2024-07-05 17:42:32 +09:00
LoveSy 4905f6d3f2 Use macos-12 because macos-11 has been removed
https://github.blog/changelog/2024-05-20-actions-upcoming-changes-to-github-hosted-macos-runners/#macos-11-deprecation-and-removal
2024-07-05 15:32:37 +08:00
Rui Ueyama cb8cc1c8f0 Refactor 2024-07-05 16:00:09 +09:00
Rui Ueyama c6b54532e9 Refactor 2024-07-04 16:17:25 +09:00
Rui Ueyama 3936134823 Refactor 2024-07-04 15:08:37 +09:00
Rui Ueyama 55ca05bab6 Refactor 2024-07-04 13:59:29 +09:00
LoveSy 7305a5c5dd Fix cross-compile lto on MacOS 2024-07-03 21:55:22 +08:00
Rui Ueyama b145377c39 Merge pull request #1296 from marxin/improve-gdb-index-test
Improve --gdb-index test by using readelf --debug=gdb_index
2024-07-01 17:15:51 +09:00
Martin Liska 08f2a74be7 Improve --gdb-index test by using readelf --debug=gdb_index 2024-06-30 16:07:47 +02:00
Rui Ueyama 82fb10fe26 Fix a test 2024-06-28 15:57:10 +09:00
Rui Ueyama 34f4d654d3 Add an entry for AlmaLinnux 2024-06-28 15:54:39 +09:00
Rui Ueyama 091395df33 Link with -ldl for dlopen()
Fixes https://github.com/rui314/mold/issues/1293
2024-06-28 11:00:37 +09:00
Rui Ueyama a38be94064 Simplify 2024-06-27 19:51:06 +09:00
Rui Ueyama cf1c02d097 Bump mold version to 2.32.1 2024-06-27 14:04:26 +09:00
Rui Ueyama 3dbe2ac704 Do not write relocation addends for RISC-V
Probably due to a bug in glibc, static PIE binaries crash on startup
in some environment.

Fixes https://github.com/rui314/mold/issues/1291
2024-06-27 13:09:58 +09:00
Rui Ueyama 35d67a3fab Add a missing #include for getenv() 2024-06-26 15:10:54 +09:00
Rui Ueyama f60fada438 Refactor 2024-06-24 16:09:35 +09:00
Rui Ueyama c419dcde1b Merge pull request #1289 from yujincheng08/main
ci: add msys2
2024-06-24 09:49:34 +09:00
LoveSy ebfe4f13c6 fix mingw compiles 2024-06-23 15:40:28 +00:00
LoveSy 8f9b59cbe6 ci: add msys2 2024-06-23 23:21:50 +08:00
Rui Ueyama 63f26708fd Unbreak daily buildbot 2024-06-23 12:38:59 +09:00
Rui Ueyama 07b09a66d3 Attempt to fix CI 2024-06-22 13:51:30 +09:00
Rui Ueyama 4cc61361f3 Refactor 2024-06-22 13:08:43 +09:00
Rui Ueyama 5535e7a6b7 Style change 2024-06-22 10:56:36 +09:00
Rui Ueyama 06b592683c Make weak undefined symbols to mark shared libraries as "needed"
It is not clearly defined when undefined weak symbols are resolved.
It looks like there are two possible approaches:

 1. Promote all weak undefined symbols to dynamic ones so that they'll
    have another chance to be resolved at load-time, or

 2. Promote weak undefined symbols to dynamic ones only when there are
    definitions in other DSOs at link-time.

(1) provides the maximum flexibility. For example, consider a main program
that has a weak undefined symbol `foo` and there's no DSO that defines it
at link-time. In (1), `foo` gets promoted to a dynamic symbol, so that one
of its depending DSO is upgraded to define `foo`, the main executable's
`foo` is resolved to that symbol at load-time. On the other hand, in (2),
`foo` would have already been converted to an absolute symbol at address
zero at link-time, so you need to rebuild the main executable to use the
new definition of `foo` in the shared library.

However, (1) is not compatible with copy relocations. This is because we
need to know the size of the symbol when creating a copy relocation, but
that information is not available unless we have a definition. It's also
not compatible with canonical PLTs because canonical PLTs have non-zero
addresses and therefore weak undefined symbols would always be resolved to
non-zero addresses.

As a workaround, GNU ld promotes weak undefs to dynamic symbols only when
they don't need copy relocations or canonical PLTs. In other words, weak
undef's behavior is different between -fPIC and -fno-PIC. In the former
case, they become dynamic symbols, and vice versa.

I don't think that workaround is a good one. So, mold took the second
approach.

There is, however, another thing to consider. What if we can find a
defined symbol in a DSO that is specified as `-as-needed`? Previously,
mold did not mark the library as "needed" and converted the weak undef
into an absolute symbol.

However, libstdc++ assumes that if weak undef symbol
`__pthread_key_create` is not resolved, it assumes that multi-threading is
not used in the executable, which resulted in a mis-detection with mold.

Therefore, this patch changes the mold's behavior so that it makes weak
undefs to keep DSOs "needed".

Fixes https://github.com/rui314/mold/issues/1286
2024-06-22 10:46:17 +09:00
Rui Ueyama 0b5e5dcbb5 Fix -Wunused-result 2024-06-22 10:46:17 +09:00
Rui Ueyama b1695c6018 Merge pull request #1287 from yujincheng08/lto
Support lto on mingw
2024-06-22 07:54:25 +09:00
LoveSy 48ee8e8257 Handle stackoverflow 2024-06-21 17:42:33 +08:00
LoveSy 50bf031d49 Support lto on mingw 2024-06-21 17:42:27 +08:00
Rui Ueyama bf05198d53 Merge pull request #1285 from yujincheng08/patch-2 2024-06-20 11:17:47 +09:00
Rui Ueyama f9a37e9dd4 Remove dead code
get_self_path() is used by `mold -run`, which is not available on
macOS or Windows.
2024-06-20 09:31:44 +09:00
LoveSy cf9e59954b Use __has_include instead 2024-06-19 23:50:40 +08:00
LoveSy 7cce297318 Only define STDERR_FILENO when not defined 2024-06-19 23:48:14 +08:00
Rui Ueyama 1f94716fce Attempt to fix CI 2024-06-18 22:40:52 +09:00
Rui Ueyama 863893057e Allow numbers other than 1 for MOLD_JOBS 2024-06-18 18:28:28 +09:00
Rui Ueyama b83b1950c0 Merge pull request #1281 from ziyao233/outgoing/fix-static-flag 2024-06-17 08:10:20 +09:00
Yao Zi d2774b0da9 Use $CXX to detect cxxflags
Use $CXX instead of $CC to detect whether C++ programs could be built
statically. Add function test_cxxflags for convenience.

Fix test error in an environment where C++ programs cannot be built
statically, with error like:

  mold: fatal: library not found: c++

Signed-off-by: Yao Zi <[email protected]>
2024-06-16 20:16:34 +00:00
PerseoGI cd7287f0de Update README adding Conan usage instructions 2024-06-13 11:26:14 +02:00
Rui Ueyama cff858bad5 Attempt to fix CI 2024-06-13 16:47:50 +09:00
Rui Ueyama 7669a3ce67 Attempt to fix CI 2024-06-13 16:11:48 +09:00
Rui Ueyama 3ad387f20c Refactor 2024-06-13 13:33:45 +09:00
Rui Ueyama af6d356468 Attempt to fix CI 2024-06-11 14:49:53 +09:00
Rui Ueyama 768f1dffdd Attempt to fix CI 2024-06-11 14:43:54 +09:00
Rui Ueyama 2a25c81834 Refactor 2024-06-11 13:37:21 +09:00
Rui Ueyama 24b8941f60 Refactor 2024-06-10 20:36:40 +09:00
LoveSy 841a186346 Fix corrupted .riscv.attributes ISA string (#1276)
Fix corrupted .riscv.attributes ISA string
extension name can contain digits
2024-06-10 19:56:40 +09:00
Rui Ueyama ec04bfda9d Bump mold version to 2.32.0 2024-06-09 12:44:21 +09:00
Rui Ueyama 8cd85aaa29 Make --undefined=ignore-in-object-files to override -z defs
The interaction between these flags are unnecessarily complicated,
but it looks like `--undefined=ignore-in-object-files` needs to
share the same internal flag as `-z defs` so that they override
each other.

Fixes https://github.com/rui314/mold/issues/1270
2024-05-31 22:00:03 +09:00
Rui Ueyama 01bc0b522d Update mold.1 (automated commit) 2024-05-31 04:53:42 +00:00
Rui Ueyama 48e777ad7b Update man page 2024-05-31 13:51:59 +09:00
Rui Ueyama 0955c30852 Update mold.1 (automated commit) 2024-05-30 09:51:42 +00:00
Rui Ueyama 02ec7156c1 Update man page 2024-05-30 18:50:35 +09:00
Rui Ueyama c7c8583946 Allow larger range extension thunks
Fixes https://github.com/rui314/mold/issues/1224
2024-05-27 15:43:43 +09:00
Rui Ueyama 08b0a1629d Do not emit incorrect relocations for mergeable sections with --relocatable
Fixes https://github.com/rui314/mold/issues/1265
2024-05-24 13:01:10 +09:00
Rui Ueyama 02b439af48 Upgrade bundled mimalloc to 2.1.6
Fixes https://github.com/rui314/mold/issues/1257
2024-05-21 11:55:39 +09:00
Rui Ueyama a8cd2e84e0 Support R_PPC64_TPREL16_LO_DS
Fixes https://github.com/rui314/mold/issues/1262
2024-05-21 11:22:51 +09:00
Rui Ueyama a25226eb13 Replace our SipHash implementation with https://github.com/rui314/siphash 2024-05-18 12:15:26 +09:00
Rui Ueyama 91f9c4d167 Avoid unaligned memory access 2024-05-17 15:39:19 +09:00
Rui Ueyama 416b0d336f Optimize
Teaching the compiler that `buflen` cannot be negative and also doesn't
take a large value allows it to optimize code a little bit more than
it currently does.
2024-05-16 11:52:58 +09:00
Rui Ueyama ca4738d5e2 Use Siphash-1-3 instead of SipHash-2-4
I couldn't find any report that the reduced round version is broken
despite being used at a lot of places (e.g. Rust's hash table uses
SipHash-1-3). So I assume it's secure.
2024-05-16 11:44:25 +09:00
Rui Ueyama 4281f45f06 Do not record response files as dependency files
Fixes https://github.com/rui314/mold/issues/1258
2024-05-16 10:31:21 +09:00
Rui Ueyama 08be7ff2d2 Use SipHash-2-4 instead of SipHash-1-3
Because I wasn't not sure SipHash-1-3 is considered as a secure MAC.
2024-05-12 09:11:32 +09:00
Rui Ueyama 47030cf6ca Fix thread-unsafe code
BitVector is not therad-safe if two threads write to the same byte
at the same time.
2024-05-11 19:56:07 +09:00
Rui Ueyama a5e5ad75fe Refactor 2024-05-11 19:56:07 +09:00
Rui Ueyama 0ce1c78012 Refactor 2024-05-11 16:54:54 +09:00
Rui Ueyama a250aea2f9 Refactor 2024-05-11 16:38:13 +09:00
Rui Ueyama 1fa4e34cec Inline memcpy
Teaching the compiler that `buflen` can never be negative here allows
it to inline subsequent `memcpy` function calls.
2024-05-11 16:28:46 +09:00
Rui Ueyama 1290a02a4e Refactor 2024-05-11 15:22:56 +09:00
Rui Ueyama 10b69883bd Refactor 2024-05-11 14:39:09 +09:00
Rui Ueyama fa8e95a289 Use SipHash instead of Blake3 for Identical Code Folding 2024-05-11 14:39:09 +09:00
Rui Ueyama 6373eb05a7 Refactor 2024-05-10 15:51:22 +09:00
Rui Ueyama 3ec4b1787e Refactor 2024-05-08 16:43:42 +09:00
Rui Ueyama 74129bfc86 Fix a test
Fixes https://github.com/rui314/mold/issues/1249
2024-05-06 13:54:17 +09:00
Rui Ueyama 595f98da78 Do not attempt to run an executable built with -z rodynamic
Such executables seem to crash immediately on older systems.
2024-05-06 13:12:33 +09:00
Rui Ueyama 2649b69e95 Fix CI 2024-05-05 22:43:58 +09:00
Rui Ueyama d20dc21e2a Update mold.1 (automated commit) 2024-05-05 13:22:52 +00:00
Rui Ueyama 7ac0816aa9 Fix typo 2024-05-05 22:21:56 +09:00
Rui Ueyama 154b6b047d Update mold.1 (automated commit) 2024-05-05 13:19:41 +00:00
Rui Ueyama 9a233df7e2 Support -z rodynamic
LLVM lld supports the flag.

Fixes https://github.com/rui314/mold/issues/1253
2024-05-05 22:17:52 +09:00
Rui Ueyama 7b2cb47727 Refactor 2024-05-05 13:08:04 +09:00
Rui Ueyama d683e8aa0b Add a test for SHF_GNU_RETAIN 2024-05-05 12:41:55 +09:00
Rui Ueyama 5ec3d2dcc2 Do not use std::osyncstream 2024-05-04 20:37:28 +09:00
Rui Ueyama edcf252603 Rename SyncOut -> Out 2024-05-04 18:16:04 +09:00
Rui Ueyama 024b03791a Remove extraneous newlines from "symbol missing" errors 2024-05-04 18:09:25 +09:00
Rui Ueyama 230ef819c2 Refactor 2024-05-04 18:08:09 +09:00
Rui Ueyama 5bdf48695a Use std::osyncstream 2024-05-04 17:42:47 +09:00
Rui Ueyama 78fea38b12 Remove a use of thread-local variable 2024-05-04 17:42:46 +09:00
Rui Ueyama 4510f4af81 Use pause instruction on ARM7 and ARM8
https://github.com/rui314/mold/issues/1250
2024-05-04 13:09:36 +09:00
Rui Ueyama a3480e8a2b Merge pull request #1251 from wahjava/fix-on-freebsd
Fix build on FreeBSD
2024-05-04 12:48:30 +09:00
Ashish SHUKLA 5396667ad5 Fix build on FreeBSD 2024-05-03 15:44:26 +00:00
Rui Ueyama f0f61cc7ad Do not use yield asm instruction on arm32
`yield` may not exist on older ARM processors.

Fixes https://github.com/rui314/mold/issues/1250
2024-05-03 21:44:27 +09:00
Rui Ueyama b237fd1ae9 Refactor 2024-05-03 20:35:14 +09:00
Rui Ueyama 2b67afb8a3 Simplify
We no longer use `sold` as a command name.
2024-05-03 15:55:26 +09:00
Rui Ueyama 20fa8d56f5 Bump mold version to 2.31.0 2024-05-03 10:04:55 +09:00
Rui Ueyama ed1e3c06a2 Do not copy mold executable to a test directory
mold executable can be quite large (~600 MiB with debug info),
so we don't want to casually copy that file.
2024-05-03 10:04:01 +09:00
Rui Ueyama fdbe7eecf9 Do not set root as the file owner 2024-05-02 14:52:17 +09:00
Rui Ueyama 16ad212919 Update the build script
It looks like ftp.gnu.org started redirecting incoming HTTP requests
to HTTPS.
2024-05-02 14:03:37 +09:00
Rui Ueyama 5c0cce3d6c Merge pull request #1246 from thesamesam/which-hunt 2024-04-30 17:50:06 +09:00
Sam James ec0a9d09dd test: use command -v, not non-portable which
`which` isn't in POSIX and several Linux distributions are trying to
remove it from their base system, see e.g. https://lwn.net/Articles/874049/.

Just use `command -v` which is POSIX.

Signed-off-by: Sam James <[email protected]>
2024-04-30 07:54:40 +01:00
Rui Ueyama ea054ccced Allow a newline or a tab at the begining of a linker script
Previously, the first four byte must have been "printable" bytes,
which exclude newlines or tabs.
2024-04-29 19:41:11 +09:00
Rui Ueyama cbbe23aa46 Do not show "entry symbol not found" error when creating a DSO 2024-04-28 18:06:54 +09:00
Rui Ueyama 990b0daae2 Optimize Aho-Corasick string search
We use Aho-Corasick string search algorithm for version script.
If all symbols in a version script do not start with '*', we can
terminate the algorithm early.
2024-04-28 17:53:20 +09:00
Rui Ueyama a16316e916 Show warning messages in purple instead of red 2024-04-28 16:56:25 +09:00
Rui Ueyama 14952546a4 Do not edit binary files with sed
Fixes https://github.com/rui314/mold/issues/1244
2024-04-28 15:43:28 +09:00
Rui Ueyama 020b1a78d9 Do not set the .text address as an entry adddress if -e is missing
lld does not do that too.
2024-04-28 15:43:28 +09:00
Rui Ueyama 6b70e02bf5 Report an error if a mergeable/merged section is too large
We use u32 as an index into mergeable/merged sections, so we cannot
handle extremely large sections.
2024-04-28 15:43:28 +09:00
Rui Ueyama 31969a2756 Refactor 2024-04-28 15:43:28 +09:00
Rui Ueyama 53ebcd80d8 Reduce memory usage
This change saves ~1 GiB memory allocation when linking ~3.8 GiB clang-19
binary because the binary contains tons of mergeable strings for debug info.
2024-04-26 14:51:01 +09:00
Rui Ueyama d714301180 Optimize 2024-04-25 18:04:18 +09:00
Rui Ueyama 002d619b11 Attempt to fix a test failure
I believe some version of objcopy corrupts an object file when
renaming a section. In this change, I use sed instead of objcopy
as a workaround.

Fixes https://github.com/rui314/mold/issues/1244
2024-04-25 16:59:40 +09:00
Rui Ueyama 55bb6bc318 Refactor 2024-04-25 15:42:01 +09:00
Rui Ueyama 05066e5279 Refactor 2024-04-25 12:17:21 +09:00
Rui Ueyama 131993634f Simplify 2024-04-24 16:05:19 +09:00
Rui Ueyama 40f6b17eda Optimize 2024-04-24 14:32:44 +09:00
Rui Ueyama 7a8ebc4dc7 Refactor 2024-04-24 13:19:20 +09:00
Rui Ueyama a9346f5c82 Add a test 2024-04-24 12:54:54 +09:00
Rui Ueyama 3104ce1dd2 Update the rule for Gentoo 2024-04-23 22:14:17 +09:00
Rui Ueyama d213f2b25d Add a rule for Clear Linux 2024-04-23 21:49:15 +09:00
Rui Ueyama 813089047f Refactor 2024-04-23 21:49:15 +09:00
Rui Ueyama 0ed5a9842a Refactor 2024-04-23 13:42:46 +09:00
Rui Ueyama f037cd0740 Refactor 2024-04-22 17:55:57 +09:00
Rui Ueyama c9faf3d345 Optimize 2024-04-22 17:47:06 +09:00
Rui Ueyama db8758060a Fix CI 2024-04-21 16:40:54 +09:00
Rui Ueyama c8ac0919a9 Fix CI 2024-04-21 15:44:21 +09:00
Rui Ueyama a2432e125a Refactor 2024-04-21 15:23:56 +09:00
Rui Ueyama e498e2fd83 Do not create .copyrel.rel.ro if -z norelro is given 2024-04-21 14:52:51 +09:00
Rui Ueyama c65121a370 Revert "Upgrade OneTBB in ./third-party to version 2021.12.0"
This reverts commit ddc63ba9b2 because it
broke macOS build.
2024-04-19 14:31:27 +09:00
Rui Ueyama fdc935718d Upgrade zlib in ./third-party to v1.3.1 2024-04-19 13:03:36 +09:00
Rui Ueyama 18d3b64bfb Upgrade zstd in ./third-party to v1.5.6 2024-04-19 12:35:31 +09:00
Rui Ueyama ddc63ba9b2 Upgrade OneTBB in ./third-party to version 2021.12.0 2024-04-19 12:28:54 +09:00
Rui Ueyama 5576b88604 Upgrade blake3 in ./third-party to v1.5.1 2024-04-19 12:25:41 +09:00
Rui Ueyama 852be4a465 Refactor 2024-04-18 22:15:03 +09:00
Rui Ueyama fb3c6029d0 Do not link against libblake3.so if we built it from ./third-party
Fixes https://github.com/rui314/mold/issues/1238
2024-04-18 21:57:16 +09:00
Rui Ueyama 9acd9ae894 Refactor 2024-04-18 21:08:48 +09:00
Rui Ueyama d478addd2b Refactor 2024-04-18 20:47:14 +09:00
Rui Ueyama 6463a7c48a Fix non-deterministic behavior 2024-04-18 20:21:55 +09:00
Rui Ueyama 7434465334 Fix CI 2024-04-18 17:00:29 +09:00
Rui Ueyama f7b705959e Remove a pragma
It looks like Clang recognize `#pragma GCC unroll`. Instead of adding
more C preprocessor directives here, I'd like to rely on the default
compiler optimizations.
2024-04-18 16:11:02 +09:00
Rui Ueyama 1648f716f4 Attempt to fix CI 2024-04-18 16:00:11 +09:00
Rui Ueyama 108b08fadb Fix a test 2024-04-18 14:47:36 +09:00
Rui Ueyama 204566cd14 Fix a crash bug involving synthesized symbols
If a synthesized symbol overrides an imported symbol, the symbol's
`is_imported` flag must be reset.

Technically, `compute_import_export` is called too early, and this
function call should be moved after `create_synthetic_sections`.
However, fixing the issue that way was very hard because until we
create output sections, we don't know what __start_/__stop_ symbols
need to be synthesized, but in order to create output sections,
we need to compute import/export bits.

So, in this commit, we simply reset `is_imported` flag for
synthesized symbols.

https://github.com/rui314/mold/pull/1236
2024-04-18 14:45:58 +09:00
Rui Ueyama a3b4192e15 Revert "Give symbols in .so higher priority than those in .a"
This reverts commit dfa26d0882 because it
caused mysterious runtime issues with XWayland. I also observed that the
change affected gnupg and others.

Fixes https://github.com/rui314/mold/issues/1235
2024-04-15 21:42:52 +09:00
Rui Ueyama dfa26d0882 1;10;0cGive symbols in .so higher priority than those in .a
Previously, .so and .a were of the same priority and therefore symbols
in those were resolved based on their positions in the command line;
whichever file appears first in the command line took precedence.

Here is the problem we are trying to solve with this change: KiCad
passes unnecessary .a files to the linker along with some .so files.
Some symbols are defined both by .a and by .so.

If a symbol is resolved from .a, the linker pulls out the file from
the archive, but because the .a file does not really provide a
complete set of object files, it ended with "undefined symbol" error.

If a symbol is resolved from .so, everything is fine.

This is arguably a bug in KiCad, or at least depending on the order of
files in the command line is very fragile. But maybe this change could
fix the issue without too much side effects. So let's see how it goes.

Fixes https://github.com/rui314/mold/discussions/1234
2024-04-15 12:19:50 +09:00
Rui Ueyama 2683a07955 Refactor 2024-04-14 19:11:54 +09:00
Rui Ueyama 62bfc5fd8c Remove unnecessary dependencies 2024-04-13 21:13:13 +09:00
Rui Ueyama f666282b4e Silence -Wunused-result
Fixes https://github.com/rui314/mold/issues/1228
2024-04-13 17:30:56 +09:00
Rui Ueyama 77aefdf6a6 Refactor 2024-04-13 17:30:32 +09:00
Rui Ueyama 60ce1aaf01 Improve a test
We want to make sure that absolute symbols in DSOs are dynamically
resolved.
2024-04-12 16:36:40 +09:00
Rui Ueyama 496e2f3eb3 Refactor 2024-04-11 14:26:37 +09:00
Rui Ueyama 93bae76f49 Update mold.1 (automated commit) 2024-04-10 07:04:37 +00:00
Rui Ueyama 7d17aa83eb Add --Bsymbolic-non-weak and --Bsymbolic-non-weak-functions
GNU ld doesn't support these options, but LLVM lld do.
2024-04-10 16:00:19 +09:00
Rui Ueyama 7835e45ded Update mold.1 (automated commit) 2024-04-10 05:28:22 +00:00
Rui Ueyama 0cf209002c Update the man page 2024-04-10 14:27:50 +09:00
Rui Ueyama 57f2260301 Update mold.1 (automated commit) 2024-04-09 12:24:04 +00:00
Rui Ueyama 76445ce73c Update the man page 2024-04-09 21:23:28 +09:00
Rui Ueyama 99a5b1541e Support -z start-stop-visibility=hidden
Fixes https://github.com/rui314/mold/issues/826
2024-04-08 13:11:15 +09:00
Rui Ueyama d432e987a0 Ignore --long-plt option
Our ARM32 PLT is always long, so we can safely ignore the flag.

Fixes https://github.com/rui314/mold/issues/1230
2024-04-06 10:29:38 +09:00
Rui Ueyama 71457375e4 Simplify 2024-04-05 22:37:12 +09:00
Rui Ueyama 8dc311c2d2 Attempt to fix CI 2024-04-05 22:31:27 +09:00
Rui Ueyama faa5e57913 Simplify 2024-04-05 21:08:57 +09:00
Rui Ueyama 4396784dbd Handle STT_COMMON as a synonym for STT_OBJECT 2024-04-05 20:33:13 +09:00
Rui Ueyama 81b714c4f3 Simplify 2024-04-03 15:29:14 +09:00
Rui Ueyama cb739c62a9 Refactor 2024-04-01 23:59:08 +09:00
Rui Ueyama 58cfc6ba39 Make RISC-V build reproducible 2024-03-30 16:28:46 +09:00
Rui Ueyama fafc315dac Simplify 2024-03-29 21:52:52 +09:00
Rui Ueyama ffcd64792d Update mold.1 (automated commit) 2024-03-29 11:14:42 +00:00
Rui Ueyama 8c24b22b2d Update the man page 2024-03-29 18:03:20 +09:00
Rui Ueyama 6fe9bdc3db Simplify 2024-03-29 17:43:06 +09:00
Rui Ueyama 9d8b243e39 Keep symbols specified by --undefined-glob during garbage collection 2024-03-29 17:39:12 +09:00
Rui Ueyama 9884e19b7f Attempt to fix Windows bot 2024-03-29 17:39:12 +09:00
Rui Ueyama 5304a343e4 Simplify 2024-03-29 15:42:10 +09:00
Rui Ueyama 38e8f06def Use trap instructions instead of nop to fill gaps 2024-03-29 15:39:59 +09:00
Rui Ueyama b93b85bcd5 Refactor 2024-03-29 12:38:57 +09:00
Rui Ueyama 587360662c Refactor 2024-03-29 11:25:36 +09:00
Rui Ueyama 95431aa7cf Do not run certain tests on qemu user-mode emulation
Fixes https://github.com/rui314/mold/pull/1206
2024-03-28 23:29:53 +09:00
Rui Ueyama 7de092df28 Remove dead code 2024-03-28 18:01:03 +09:00
Rui Ueyama 03b57c2655 Simplify 2024-03-28 17:47:28 +09:00
Rui Ueyama a1c835b993 Support --undefined-glob option 2024-03-28 16:44:43 +09:00
Rui Ueyama 4ada2f52da Refactor 2024-03-28 16:25:21 +09:00
Rui Ueyama 9bb1a8207a Update README.md 2024-03-28 13:08:53 +09:00
Rui Ueyama a9e76295ff Simplify 2024-03-28 11:52:33 +09:00
Rui Ueyama 4e3364ddd3 Format 2024-03-27 18:42:55 +09:00
Rui Ueyama 6e5ae45c1a Disable test on ppc64le
Fixes https://github.com/rui314/mold/issues/1221
2024-03-27 16:27:37 +09:00
Rui Ueyama 11c9376a50 Fix tests for musl libc-based systems
Fixes https://github.com/rui314/mold/issues/1221
2024-03-27 16:27:37 +09:00
Rui Ueyama b5f2a7b1ce Do not call do_lto() if all LTO input files are dead
Fixes https://github.com/rui314/mold/issues/1226
2024-03-27 15:27:52 +09:00
Rui Ueyama 3df7c8e89c Export __global_pointer$ from executable
RISC-V psABI requires that symbol to be exported from an executable
if there's a GP-relative reference. For simplicity, we always export
it from executable as long as it has a .dynamic section.

https://github.com/rui314/mold/issues/1222
2024-03-25 17:57:52 +09:00
Rui Ueyama 0edbe29d38 Simplify 2024-03-25 17:57:52 +09:00
Rui Ueyama 8df5a76203 Attempt to fix Windows CI 2024-03-25 17:57:52 +09:00
Rui Ueyama 32c4a09deb Fix file paths in .repro tar file
Previously, all pathnames were accidentally recorded as the name of
the tar file itself.

Fixes https://github.com/rui314/mold/issues/1227
2024-03-25 17:15:51 +09:00
Rui Ueyama ee6907f678 Fix "skipping incompatible file" errors
https://github.com/rui314/mold/pull/1217
2024-03-25 14:52:54 +09:00
Rui Ueyama 03640029db Refactor 2024-03-21 15:03:45 +09:00
Rui Ueyama 7a65ed9082 Refactor 2024-03-21 11:00:13 +09:00
Rui Ueyama b10f818106 Attempt to fix Windows build 2024-03-21 10:42:30 +09:00
Rui Ueyama caad70dd6f Do not group mergeable sections in an output file
Runtime doesn't care where mergeable sections are, but it's reported
that Valgrind does. So, grouping mergeable sections just doesn't seem
to make sense.

https://github.com/rui314/mold/pull/1223
2024-03-20 22:15:05 +09:00
Rui Ueyama 6fe4f23074 Attempt to fix CI 2024-03-20 18:41:57 +09:00
Rui Ueyama fd8fb73492 Refactor 2024-03-20 17:37:57 +09:00
Rui Ueyama 7c8ed4d2e1 Refactor 2024-03-20 15:05:16 +09:00
Rui Ueyama 08fada7de0 Update README.md 2024-03-20 13:44:09 +09:00
Rui Ueyama 163975d82a Make linker script INPUT command to look for a file from the script's directory
https://github.com/rui314/mold/pull/1217
2024-03-19 17:16:38 +09:00
Rui Ueyama dda521e2bc [ARM32] Show "recompile with -fPIC" error for absolute relocations
Fixes https://github.com/rui314/mold/issues/1220
2024-03-19 13:19:05 +09:00
Rui Ueyama e7dc90dadc Refactor 2024-03-18 17:10:18 +09:00
Rui Ueyama 8375741489 Merge pull request #1201 from nga888/win-mem-map-output-file
Add "basic" support for memory mapping to Windows OutputFile
2024-03-16 11:35:25 +09:00
Rui Ueyama 042b14653e Do not update the man page for branches other than main 2024-03-16 09:18:48 +09:00
Andrew Ng 37f975bf82 Add "basic" support for memory mapping to Windows OutputFile
This is a "basic" implementation that does not make use of intermediate
temporary files.

Has been seen to improve linking performance by up to ~1.78x.
2024-02-26 11:34:33 +00:00
Muhammad Mominul Huque f05e32df4a Update README.md
GCC doesn't accept a path as an argument to `-fuse-ld`. `-fuse-ld=mold` is enough.

Tested with
```
gcc (GCC) 13.2.1 20231205 (Red Hat 13.2.1-6)
```
2023-12-31 18:21:30 +06:00
3330 changed files with 63742 additions and 731676 deletions

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
# rustc's LoongArch targets use the medium code model, but the cc crate
# compiles C code with the C compiler's default normal code model, whose
# direct calls reach only ±128 MiB, unless -Ccode-model is given
# explicitly. A debug build of mold has more text than that, so we pass
# the model explicitly to make the cc crate pass -mcmodel=medium.
[target.'cfg(target_arch = "loongarch64")']
rustflags = ["-Ccode-model=medium"]
-10
View File
@@ -1,10 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
Delete this message and describe the bug here. Note that we only fix bugs in the most recent release of mold, so please make sure that you are using the most recent version.
+57 -16
View File
@@ -3,16 +3,39 @@ name: Build all tarballs
on:
schedule:
- cron: '0 0 * * *'
release:
types: [ published ]
workflow_dispatch:
jobs:
build-tarballs:
runs-on: ubuntu-latest
check-changes:
runs-on: ubuntu-24.04
outputs:
has_changes: ${{ github.event_name == 'workflow_dispatch' || steps.check.outputs.has_changes }}
steps:
- name: Check for new commits since last successful run
id: check
env:
GH_TOKEN: ${{ github.token }}
run: |
last=$(gh run list -R ${{ github.repository }} -w build-all.yml -b main -s success -L1 --json createdAt -q '.[0].createdAt')
if [ -z "$last" ] || [ "$last" = "null" ] || [ "$(gh api repos/${{ github.repository }}/commits?sha=main\&since=$last\&per_page=1 | jq length)" -gt 0 ]; then
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
build-linux:
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true'
strategy:
matrix:
target: [ x86_64, aarch64, arm, riscv64, ppc64le, s390x ]
include:
- { target: x86_64, os: ubuntu-24.04 }
- { target: aarch64, os: ubuntu-24.04-arm }
- { target: arm, os: ubuntu-24.04-arm }
- { target: riscv64, os: ubuntu-24.04 }
- { target: ppc64le, os: ubuntu-24.04 }
- { target: s390x, os: ubuntu-24.04 }
- { target: loongarch64, os: ubuntu-24.04 }
runs-on: ${{ matrix.os }}
permissions:
contents: read
@@ -20,26 +43,44 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v2
uses: actions/checkout@v7
- name: Install Podman
run: sudo apt-get update && sudo apt-get install -y podman qemu-user-static
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: redhat-actions/podman-login@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build a tarball
run: ./dist.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v7
with:
name: tarballs
path: mold-*.tar.gz
name: ${{ matrix.target }}
path: dist/mold-*.tar.gz
compression-level: 0
build-windows:
needs: check-changes
if: needs.check-changes.outputs.has_changes == 'true'
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Build and Archive
shell: pwsh
run: ./.github/workflows/build-windows.ps1
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: win-x86_64
path: dist/mold-*.*
+44
View File
@@ -0,0 +1,44 @@
name: Build native tarballs
on:
push:
workflow_dispatch:
jobs:
build-tarballs:
strategy:
matrix:
include:
- { target: x86_64, os: ubuntu-24.04 }
- { target: aarch64, os: ubuntu-24.04-arm }
- { target: arm, os: ubuntu-24.04-arm }
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v7
- name: Install Podman
run: sudo apt-get update && sudo apt-get install -y podman qemu-user-static
- name: Login to GitHub Container Registry
uses: redhat-actions/podman-login@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build a tarball
run: ./dist.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}
path: dist/mold-*.tar.gz
compression-level: 0
+16
View File
@@ -0,0 +1,16 @@
$ErrorActionPreference = 'Stop'
Set-Location "$PSScriptRoot/../.."
cargo build --release --locked --package mold-cli
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$metadata = cargo metadata --locked --no-deps --format-version 1 | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$version = ($metadata.packages | Where-Object name -eq 'mold-cli').version
$stage = 'mold-install'
New-Item -ItemType Directory -Force "$stage/bin", "$stage/share/man/man1", "$stage/share/doc/mold", dist | Out-Null
Copy-Item target/release/mold.exe "$stage/bin/"
Copy-Item docs/mold.1 "$stage/share/man/man1/"
Copy-Item LICENSE "$stage/share/doc/mold/"
Compress-Archive -Path "$stage/*" -DestinationPath "dist/mold-$version-x86_64-windows.zip" -Force
-36
View File
@@ -1,36 +0,0 @@
name: Build x86 tarball
on:
push:
branches: [ main ]
jobs:
build-tarball:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build a tarball
run: ./dist.sh
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: tarball
path: mold-*.tar.gz
+169 -118
View File
@@ -1,141 +1,192 @@
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
jobs:
build-clang:
test-all-targets:
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
# Incremental artifacts and mold's own debug info are unused in CI.
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- name: install test dependencies
run: |
sudo ./install-test-deps.sh
sudo .github/workflows/install-extras.sh
- run: cargo build --locked
- name: check the build for 32-bit hosts
run: |
rustup target add armv7-unknown-linux-gnueabihf i686-unknown-linux-gnu \
powerpc-unknown-linux-gnu
cargo check --locked --workspace --all-targets --target armv7-unknown-linux-gnueabihf
# i386 aligns 64-bit integers to 4 bytes
cargo check --locked --workspace --all-targets --target i686-unknown-linux-gnu
# 32-bit PowerPC has no 64-bit atomics
cargo check --locked --workspace --all-targets --target powerpc-unknown-linux-gnu
- name: verify all target toolchains are available
run: |
cargo test --locked -p mold-cli --test integration -- --list |
tee "$RUNNER_TEMP/test-inventory.txt"
! grep -q '^unavailable:' "$RUNNER_TEMP/test-inventory.txt"
# All workspace crates were built above. Avoid linking empty test binaries
# for the target crates and command.
- run: cargo test --locked -p mold -p mold-tests -p mold-cli --lib --test integration
- name: archive test results
uses: actions/upload-artifact@v7
if: failure()
with:
name: test-results-all-targets
path: target/debug/mold-test/out/test
test-sanitizers:
strategy:
matrix:
target:
# Disable PCH for the default configuration. This prevents relying on implicit includes.
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
- '-DMOLD_USE_ASAN=On'
- '-DMOLD_USE_TSAN=On'
runs-on: ubuntu-20.04
include:
- sanitizer: address
# Input files intentionally remain allocated until process exit.
runtime-options: detect_leaks=0:halt_on_error=1
test-prefix: ''
test-timeout: 30
- sanitizer: thread
runtime-options: halt_on_error=1
# TSan cannot model the atomic-fence synchronization in Rayon's
# work-stealing queue. Two CPUs still exercise parallel linker code.
test-prefix: taskset -c 0,1
test-timeout: 60
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
CARGO_TARGET_DIR: target/${{ matrix.sanitizer }}
RUSTFLAGS: -Zsanitizer=${{ matrix.sanitizer }}
RUSTDOCFLAGS: -Zsanitizer=${{ matrix.sanitizer }}
steps:
- uses: actions/checkout@v3
- uses: rui314/setup-mold@staging
- name: install-build-deps
run: sudo ./install-build-deps.sh
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
sudo apt-get install -y clang++-12
mkdir build
cd build
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++-12 ${{ matrix.target }} ..
cmake --build . -j$(nproc)
- name: test
run: |
cd build
ctest -j$(nproc)
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: sudo ./install-test-deps.sh
- run: cargo +nightly build --locked -Zbuild-std --target x86_64-unknown-linux-gnu -p mold-cli --no-default-features --features system-allocator,x86_64
- run: |
if [ '${{ matrix.sanitizer }}' = address ]; then
export ASAN_OPTIONS='${{ matrix.runtime-options }}'
else
export TSAN_OPTIONS='${{ matrix.runtime-options }}'
fi
${{ matrix.test-prefix }} cargo +nightly test --locked -Zbuild-std --target x86_64-unknown-linux-gnu -p mold-cli --no-default-features --features system-allocator,x86_64 --test integration -- --native --test-threads 1 --timeout ${{ matrix.test-timeout }}
- name: archive test results
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v7
if: failure()
with:
name: test-results-clang
path: |
build
!build/CMakeFiles
name: test-results-${{ matrix.sanitizer }}
path: target/${{ matrix.sanitizer }}/x86_64-unknown-linux-gnu/debug/mold-test/out/test
build-gcc:
runs-on: ubuntu-20.04
container: gcc:11.1.0
build-msan:
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- name: install-build-deps
shell: bash
run: |
# Install cross toolchains
./install-build-deps.sh
./install-cross-tools.sh
# Install a RV32 toolchain from third party since it's not available
# as an Ubuntu package.
mkdir /usr/local/rv32
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.07/riscv32-glibc-ubuntu-20.04-gcc-nightly-2023.07.07-nightly.tar.gz | tar -C /usr/local/rv32 --strip-components=1 -xzf -
ln -sf /usr/local/rv32/sysroot /usr/riscv32-linux-gnu
echo '/usr/local/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
echo '/usr/local/rv32/bin/riscv32-unknown-linux-gnu-g++ -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-g++
chmod 755 /usr/bin/riscv32-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /usr/local/rv32/bin/riscv32-unknown-linux-gnu-$i /usr/bin/riscv32-linux-gnu-$i
done
# Install a LoongArch toolchain
mkdir /usr/local/larch
wget -O- -q https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz | tar -C /usr/local/larch --strip-components=1 --xz -xf -
ln -sf /usr/local/larch/target /usr/loongarch64-linux-gnu
cp -r /usr/local/larch/loongarch64-unknown-linux-gnu/lib/* /usr/loongarch64-linux-gnu/lib64/
for i in objdump objcopy strip; do
ln -sf /usr/local/larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
done
echo '/usr/local/larch/bin/loongarch64-unknown-linux-gnu-gcc -L/usr/local/larch/loongarch64-unknown-linux-gnu "$@"' > /usr/bin/loongarch64-linux-gnu-gcc
echo '/usr/local/larch/bin/loongarch64-unknown-linux-gnu-g++ -L/usr/local/larch/loongarch64-unknown-linux-gnu "$@"' > /usr/bin/loongarch64-linux-gnu-g++
chmod 755 /usr/bin/loongarch64-linux-gnu-{gcc,g++}
wget -O/usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2023.08.08/qemu-loongarch64
chmod 755 /usr/local/bin/qemu-loongarch64
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake ..
cmake --build . -j$(nproc)
- name: test
run: |
cd build
ctest -j$(nproc)
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@nightly
with:
components: rust-src
- run: sudo ./install-test-deps.sh
- run: sudo apt-get install -y clang llvm
- run: .github/workflows/run-msan.sh
- name: archive test results
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v7
if: failure()
with:
name: test-results-gcc
path: |
build
!build/CMakeFiles
name: test-results-msan
path: target/msan/x86_64-unknown-linux-gnu/debug/mold-test/out/test
test-distros:
strategy:
matrix:
distro:
- alpine
- archlinux
- debian:12
- fedora
- gentoo/stage3
- opensuse/tumbleweed
- ubuntu:22.04
- ubuntu:25.04
runs-on: ubuntu-latest
timeout-minutes: 60
# Tumbleweed does not declare PATH in its container configuration.
container:
image: ${{ matrix.distro }}
env:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- uses: actions/checkout@v6
- run: ./install-test-deps.sh
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --locked -p mold-cli --no-default-features --features x86_64
- run: cargo test --locked -p mold-cli --no-default-features --features x86_64 --test integration -- --native
- name: sanitize distro name
if: failure()
id: sanitize
run: echo "name=$(echo '${{ matrix.distro }}' | tr '/:' '--')" >> "$GITHUB_OUTPUT"
- name: archive test results
uses: actions/upload-artifact@v7
if: failure()
with:
name: test-results-${{ steps.sanitize.outputs.name }}
path: target/debug/mold-test/out/test
build-macos:
runs-on: macos-11
strategy:
matrix:
target:
# Disable PCH for the default configuration. This prevents relying on implicit includes.
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
- '-DMOLD_USE_ASAN=On'
runs-on: macos-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake ${{ matrix.target }} ..
cmake --build . -j$(sysctl -n hw.physicalcpu)
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --locked -p mold-cli --no-default-features --features x86_64
build-windows:
runs-on: windows-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- name: build
run: |
mkdir build
cd build
cmake -T clangcl ..
cmake --build . -j $Env:NUMBER_OF_PROCESSORS
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --locked -p mold-cli --no-default-features --features x86_64
build-msys:
runs-on: windows-latest
timeout-minutes: 60
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
steps:
- uses: actions/checkout@v6
- uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: git
pacboy: rust:p cc:p dlfcn:p
- shell: msys2 {0}
run: cargo build --locked -p mold-cli --no-default-features --features x86_64
build-freebsd:
runs-on: ubuntu-latest
timeout-minutes: 60
env:
# The VM has little disk space, so omit unused build artifacts.
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
steps:
- uses: actions/checkout@v6
- uses: vmactions/freebsd-vm@v1
with:
usesh: true
run: |
pkg install -y rust
./install-test-deps.sh
cargo build --locked -p mold-cli --no-default-features --features x86_64
cargo test --locked -p mold-cli --no-default-features --features x86_64 --test integration -- --native
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash -x
apt-get update
apt-get install -y wget xz-utils
# Install a 32-bit RISC-V toolchain
mkdir /rv32
wget -O- --progress=dot:mega https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.06.13/riscv32-glibc-ubuntu-24.04-gcc-nightly-2025.06.13-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
ln -sf /rv32/sysroot /usr/riscv32-linux-gnu
echo '/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
echo '/rv32/bin/riscv32-unknown-linux-gnu-g++ -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-g++
chmod 755 /usr/bin/riscv32-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /rv32/bin/riscv32-unknown-linux-gnu-$i /usr/bin/riscv32-linux-gnu-$i
done
# Install a LoongArch toolchain
mkdir /larch
wget -O- --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2025.06.06/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_15.1.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
cp -r /larch/loongarch64-unknown-linux-gnu/lib/* /larch/target/lib64
ln -sf /larch/target /usr/loongarch64-linux-gnu
for i in gcc g++ objdump objcopy strip; do
ln -sf /larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
done
wget -O /usr/local/bin/qemu-loongarch64 --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2025.06.06/qemu-loongarch64
chmod 755 /usr/local/bin/qemu-loongarch64
# Install ARM32 big-endian toolchain
mkdir /armeb
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/armebv7-eabihf/tarballs/armebv7-eabihf--glibc--stable-2024.05-1.tar.xz | tar -C /armeb --strip-components=1 --xz -xf -
ln -sf /armeb/armeb-buildroot-linux-gnueabihf/sysroot /usr/armeb-linux-gnueabihf
echo '/armeb/bin/armeb-linux-gcc -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-gcc
echo '/armeb/bin/armeb-linux-g++ -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-g++
chmod 755 /usr/bin/armeb-linux-gnueabihf-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /armeb/bin/armeb-linux-$i /usr/bin/armeb-linux-gnueabihf-$i
done
# Install ARM64 big-endian toolchain
mkdir /aarch64be
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64be/tarballs/aarch64be--glibc--stable-2024.05-1.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
ln -sf /aarch64be/aarch64_be-buildroot-linux-gnu/sysroot /usr/aarch64_be-linux-gnu
echo '/aarch64be/bin/aarch64_be-linux-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
echo '/aarch64be/bin/aarch64_be-linux-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
chmod 755 /usr/bin/aarch64_be-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /aarch64be/bin/aarch64_be-linux-$i /usr/bin/aarch64_be-linux-gnu-$i
done
# Install SH4 big-endian toolchain
mkdir /sh4aeb
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
ln -sf /sh4aeb/sh4aeb-buildroot-linux-gnu/sysroot /usr/sh4aeb-linux-gnu
echo '/sh4aeb/bin/sh4aeb-linux-gcc -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-gcc
echo '/sh4aeb/bin/sh4aeb-linux-g++ -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-g++
chmod 755 /usr/bin/sh4aeb-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /sh4aeb/bin/sh4aeb-linux-$i /usr/bin/sh4aeb-linux-gnu-$i
done
# Install Intel SDE CPU emulator for CET-related tests
mkdir /sde
wget -O- --progress=dot:mega https://downloadmirror.intel.com/850782/sde-external-9.53.0-2025-03-16-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
ln -s /sde/sde64 /usr/bin
+97
View File
@@ -0,0 +1,97 @@
name: Build & attach tarballs on tag push
on:
push:
tags: ['v[0-9]*']
permissions:
contents: write
packages: write
id-token: write
env:
TAG: ${{ github.ref_name }}
jobs:
build-linux:
strategy:
matrix:
include:
- { target: x86_64, os: ubuntu-24.04 }
- { target: aarch64, os: ubuntu-24.04-arm }
- { target: arm, os: ubuntu-24.04-arm }
- { target: riscv64, os: ubuntu-24.04 }
- { target: ppc64le, os: ubuntu-24.04 }
- { target: s390x, os: ubuntu-24.04 }
- { target: loongarch64, os: ubuntu-24.04 }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Install Podman
run: sudo apt-get update && sudo apt-get install -y podman qemu-user-static
- name: Login to GitHub Container Registry
uses: redhat-actions/podman-login@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: ./dist.sh ${{ matrix.target }}
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}
path: dist/mold-*.tar.gz
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Build and Archive
shell: pwsh
run: ./.github/workflows/build-windows.ps1
- uses: actions/upload-artifact@v7
with:
name: win-x86_64
path: dist/mold-*.*
publish:
needs: [build-linux, build-windows]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
path: dist
pattern: '*'
merge-multiple: true
- name: Ensure release exists (create if missing)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -e
if gh release view "$TAG" >& /dev/null; then
echo "Existing release found for $TAG"
else
echo "Creating draft release for $TAG"
gh release create "$TAG" --draft \
--title "$TAG" \
--notes "Automated draft – fill the changelog later."
fi
- name: Upload tarballs
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "$TAG" dist/* --clobber
gh release view "$TAG" --json assets --jq '.assets[].name'
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
set -euo pipefail
cd "$(dirname "$0")/../.."
# MSan requires instrumented Rust, std, and native dependencies:
# https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html#memorysanitizer
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target/msan}"
export RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins=2'
export RUSTDOCFLAGS="$RUSTFLAGS"
export CC_x86_64_unknown_linux_gnu=clang
export CFLAGS_x86_64_unknown_linux_gnu='-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer'
export LIBZ_SYS_STATIC=1
export MSAN_OPTIONS=halt_on_error=1
# The instrumented linker is too slow unoptimized for the heaviest tests
# to finish within the per-test timeout.
export CARGO_PROFILE_DEV_OPT_LEVEL=1
# Use instrumentable implementations instead of BLAKE3/Zstd assembly, and
# select mold as well as mold-cli so Cargo enables its dependency features.
args=(
--locked -Zbuild-std --target x86_64-unknown-linux-gnu
--package mold --package mold-cli --no-default-features
--features mold-cli/system-allocator,mold-cli/x86_64,blake3/pure,zstd/no_asm
--test integration
)
cargo +nightly test "${args[@]}" --no-run
# The preload wrapper runs inside uninstrumented compiler processes, not
# inside mold. Rebuild it without MSan so those processes can load it.
clang -shared -fPIC -o "$CARGO_TARGET_DIR/x86_64-unknown-linux-gnu/debug/mold-wrapper.so" \
c/mold-wrapper.c -ldl
cargo +nightly test "${args[@]}" -- --native --test-threads 1 --timeout 60 "$@"
+8 -4
View File
@@ -1,11 +1,15 @@
# This file is generated by ChatGPT
name: Update manpage
on:
push:
paths:
- 'docs/mold.md'
branches:
- main
workflow_dispatch:
permissions:
contents: write
jobs:
update-manpage:
@@ -13,10 +17,10 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v2
uses: actions/checkout@v7
- name: Install ronn
run: sudo apt-get install -y ronn
run: sudo apt-get update && sudo apt-get install -y ronn
- name: Generate mold.1 from mold.md
run: ronn --roff docs/mold.md
+5 -9
View File
@@ -1,10 +1,6 @@
*.o
*.so
/target
/dist
/bench
/.claude
/.codex
*~
**/.DS_Store
.cache
core
gentoo
/mold-*.tar.gz
/build*
/mold
-438
View File
@@ -1,438 +0,0 @@
# You can customize a build by specifying CMake options. An option may be
# given in the -Dvariable=value form. For a boolean variable, `ON` or `1`
# means true, while `OFF` or `0` means false.
#
# Here are a couple of common cmake options:
#
# -DCMAKE_C_COMPILER=<command-name>
#
# Specifies the C compiler name to use. The default value is `cc`.
#
# -DCMAKE_CXX_COMPILER=<command-name>
#
# Specifies the C++ compiler name to use. The default value is `c++`.
#
# -DCMAKE_INSTALL_PREFIX=<directory>
#
# Specifies the install target directory. The default value is `/usr/local`.
#
# -DCMAKE_BUILD_TYPE=[Debug | Release | RelWithDebInfo | MinSizeRel]
#
# Specifies the build type. The default is `Release`, which is the right
# option unless you are debugging mold.
#
# An example of a cmake command line is shown below:
#
# $ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_CXX_COMPILER=g++-12 ..
#
# where `..` refers to this directory.
#
# With cmake, you may run `cmake --install .` instead of `make install` to
# install build artifacts to system directories. If you want to install
# artifacts to a temporary target directory, run `cmake --install . --prefix
# <dir>`.
#
# You can see the current cmake variables and their values by running
# `cmake -N -L .` in a build directory.
#
# Note that in this file, we provide various dials and knobs to configure
# how to build mold. However, as a policy, we do not provide a way to
# enable/disable any individual mold's feature. In other words, we do not
# provide options like `--enable-foo` or `--disable-foo`. The motivation
# behind this is build reproducibility. We want to guarantee that all builds
# of the mold linker of the same version will have the exact same set of
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.30.0)
include(CMakeDependentOption)
include(CheckSymbolExists)
include(GNUInstallDirs)
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
if(MOLD_USE_MOLD)
add_link_options(-fuse-ld=mold -Wl,--gdb-index)
endif()
add_executable(mold)
target_compile_features(mold PRIVATE cxx_std_20)
target_link_libraries(mold PRIVATE ${CMAKE_DL_LIBS})
if(NOT "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
target_compile_options(mold PRIVATE
-fno-exceptions
-fno-unwind-tables
-fno-asynchronous-unwind-tables
-Wall
-Wextra
-Wno-sign-compare
-Wno-unused-function
-Wno-unused-parameter
-Wno-missing-field-initializers
-ggnu-pubnames)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(mold PRIVATE -D_GLIBCXX_ASSERTIONS)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
set(OPENBSD ON)
endif()
# Build mold with -flto if -DMOLD_LTO=ON
option(MOLD_LTO "Build mold with link-time optimization enabled")
if(MOLD_LTO)
set_property(TARGET mold PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
endif()
# Enable AddressSanitizer if -DMOLD_USE_ASAN=ON
option(MOLD_USE_ASAN "Build mold with AddressSanitizer" OFF)
if(MOLD_USE_ASAN)
target_compile_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
target_link_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
endif()
# Enabled ThreadSanitizer if -DMOLD_USE_TSAN=ON
option(MOLD_USE_TSAN "Build mold with ThreadSanitizer" OFF)
if(MOLD_USE_TSAN)
target_compile_options(mold PRIVATE -fsanitize=thread)
target_link_options(mold PRIVATE -fsanitize=thread)
endif()
# Statically-link libstdc++ if -DMOLD_MOSTLY_STATIC=ON.
#
# This option is intended to be used by `./dist.sh` script to create a
# mold binary that works on various Linux distros. You probably don't
# need nor want to set this to ON.
option(MOLD_MOSTLY_STATIC "Statically link libstdc++ and libcrypto" OFF)
if(MOLD_MOSTLY_STATIC)
target_link_options(mold PRIVATE -static-libstdc++)
endif()
# Find zlib. If libz.so is not found, we compile a bundled one and
# statically-link it to mold.
find_package(ZLIB QUIET)
if(ZLIB_FOUND AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE ZLIB::ZLIB)
else()
add_subdirectory(third-party/zlib EXCLUDE_FROM_ALL)
target_include_directories(zlibstatic INTERFACE third-party/zlib
$<TARGET_PROPERTY:zlibstatic,BINARY_DIR>)
target_link_libraries(mold PRIVATE zlibstatic)
endif()
# Find BLAKE3 cryptographic hash library. Just like zlib, if libblkae3.so
# is not found, we compile a bundled one and statically-link it to mold.
find_package(BLAKE3 QUIET)
if(BLAKE3_FOUND AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE BLAKE3::blake3)
else()
add_subdirectory(third-party/blake3/c EXCLUDE_FROM_ALL)
target_link_libraries(mold PRIVATE blake3)
target_include_directories(mold PUBLIC third-party/blake3/c)
endif()
# Find zstd compression library. If zstd.h is not found, we compile a
# bundled one and statically-link it to mold.
include(CheckIncludeFile)
check_include_file(zstd.h HAVE_ZSTD_H)
if(HAVE_ZSTD_H AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE zstd)
else()
add_subdirectory(third-party/zstd/build/cmake EXCLUDE_FROM_ALL)
target_compile_definitions(libzstd_static PRIVATE
ZSTD_BUILD_STATIC=1
ZSTD_BUILD_SHARED=0
ZSTD_BUILD_PROGRAMS=0
ZSTD_MULTITHREAD_SUPPORT=0
ZSTD_BUILD_TESTS=0)
target_include_directories(mold PUBLIC third-party/zstd/lib)
target_link_libraries(mold PRIVATE libzstd_static)
endif()
# Find mimalloc. mimalloc is an alternative malloc implementation
# optimized for multi-threaded applications.
#
# If you want to use the usual libc's malloc, pass -DMOLD_USE_MIMALLOC=OFF.
#
# We enable mimalloc by default for 64-bit targets. It doesn't seem to
# be stable on 32-bit targets.
cmake_dependent_option(
MOLD_USE_MIMALLOC "Use mimalloc" ON
"CMAKE_SIZEOF_VOID_P EQUAL 8; NOT APPLE; NOT ANDROID; NOT OPENBSD" OFF)
cmake_dependent_option(
MOLD_USE_SYSTEM_MIMALLOC "Use system or vendored mimalloc" OFF
MOLD_USE_MIMALLOC OFF)
# By default, we build a bundled mimalloc and statically-link it to
# mold. If you want to dynamically link to the system's
# libmimalloc.so, pass -DMOLD_USE_SYSTEM_MIMALLOC=ON.
if(MOLD_USE_MIMALLOC)
if(MOLD_USE_SYSTEM_MIMALLOC)
find_package(mimalloc REQUIRED)
target_link_libraries(mold PRIVATE mimalloc)
target_compile_definitions(mold PRIVATE USE_SYSTEM_MIMALLOC)
else()
function(mold_add_mimalloc)
set(MI_BUILD_STATIC ON CACHE INTERNAL "")
set(MI_BUILD_TESTS OFF CACHE INTERNAL "")
add_subdirectory(third-party/mimalloc EXCLUDE_FROM_ALL)
target_compile_definitions(mimalloc-static PRIVATE MI_USE_ENVIRON=0)
target_link_libraries(mold PRIVATE mimalloc-static)
endfunction()
mold_add_mimalloc()
endif()
endif()
# Find TBB. TBB (OneTBB or Intel TBB) is a high-level threading library.
# Use of this library is mandatory.
#
# By default, we build a bundled one and statically-link the library
# to mold. If you want to link to the system's libtbb2.so, pass
# -DMOLD_USE_SYSTEM_TBB=ON.
option(MOLD_USE_SYSTEM_TBB "Use system or vendored TBB" OFF)
if(MOLD_USE_SYSTEM_TBB)
find_package(TBB REQUIRED)
target_link_libraries(mold PRIVATE TBB::tbb)
else()
function(mold_add_tbb)
set(BUILD_SHARED_LIBS OFF)
set(TBB_TEST OFF CACHE INTERNAL "")
set(TBB_STRICT OFF CACHE INTERNAL "")
add_subdirectory(third-party/tbb EXCLUDE_FROM_ALL)
target_compile_definitions(tbb PRIVATE __TBB_DYNAMIC_LOAD_ENABLED=0)
target_link_libraries(mold PRIVATE TBB::tbb)
endfunction()
mold_add_tbb()
endif()
# We always use Clang to build mold on Windows. MSVC can't compile mold.
if(WIN32)
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR
"Your compiler is not supported; install Clang from Visual Studio Installer and re-run cmake with '-T clangcl'")
endif()
target_compile_definitions(mold PRIVATE NOGDI NOMINMAX)
if(MINGW)
target_compile_definitions(mold PRIVATE _WIN32_WINNT=0xA00)
target_link_libraries(mold PRIVATE bcrypt)
endif()
else()
include(CheckLibraryExists)
check_library_exists(m pow "" LIBM_FOUND)
if(LIBM_FOUND)
target_link_libraries(mold PRIVATE m)
endif()
endif()
# Build mold-wrapper.so
if(NOT APPLE AND NOT WIN32)
add_library(mold-wrapper SHARED)
install(TARGETS mold-wrapper DESTINATION ${CMAKE_INSTALL_LIBDIR}/mold)
# Remove the default `lib` prefix
set_target_properties(mold-wrapper PROPERTIES PREFIX "")
target_link_libraries(mold-wrapper PRIVATE ${CMAKE_DL_LIBS})
target_sources(mold-wrapper PRIVATE elf/mold-wrapper.c)
endif()
# If atomics doesn't work by default, add -latomic.
# We need the flag on riscv, armv6 and m68k.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("#include <atomic>
int main() {
std::atomic_uint8_t a;
std::atomic_uint16_t b;
std::atomic_uint32_t c;
std::atomic_uint64_t d;
return ++a + ++b + ++c + ++d;
}" HAVE_FULL_ATOMIC_SUPPORT)
if(NOT HAVE_FULL_ATOMIC_SUPPORT)
target_link_libraries(mold PRIVATE atomic)
endif()
# Add -pthread
if(NOT APPLE AND NOT MSVC)
target_compile_options(mold PRIVATE -pthread)
target_link_options(mold PRIVATE -pthread)
endif()
check_symbol_exists(madvise sys/mman.h HAVE_MADVISE)
# Create a .cc file containing the current git hash for `mold --version`.
add_custom_target(git_hash
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
-P ${CMAKE_SOURCE_DIR}/common/update-git-hash.cmake
DEPENDS common/update-git-hash.cmake
BYPRODUCTS git-hash.cc
VERBATIM)
add_dependencies(mold git_hash)
# Create config.h file
configure_file(common/config.h.in config.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Almost all functions are template in mold which take a target type
# (e.g. X86_64) as its type parameter. Since we suport more than 10
# targets, compiling a single source file for all the targets is very
# slow.
#
# As a workaround, we create a .cc file for each target and spawn many
# compiler instances. This is hacky but greatly reduces compile time
# on a multicore machine.
list(APPEND MOLD_ELF_TARGETS
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE PPC32 PPC64V1 PPC64V2
S390X SPARC64 M68K SH4 ALPHA LOONGARCH32 LOONGARCH64)
list(APPEND MOLD_ELF_TEMPLATE_FILES
elf/arch-loongarch.cc
elf/arch-riscv.cc
elf/cmdline.cc
elf/gc-sections.cc
elf/gdb-index.cc
elf/icf.cc
elf/input-files.cc
elf/input-sections.cc
elf/jobs.cc
elf/linker-script.cc
elf/lto.cc
elf/main.cc
elf/mapfile.cc
elf/output-chunks.cc
elf/passes.cc
elf/relocatable.cc
elf/subprocess.cc
elf/thunks.cc
elf/tls.cc
)
list(APPEND MOLD_MACHO_TARGETS X86_64 ARM64)
list(APPEND MOLD_MACHO_TEMPLATE_FILES
macho/cmdline.cc
macho/dead-strip.cc
macho/input-files.cc
macho/input-sections.cc
macho/lto.cc
macho/main.cc
macho/mapfile.cc
macho/output-chunks.cc
macho/tapi.cc
)
function(mold_instantiate_templates SOURCE TARGET)
set(PATH ${CMAKE_BINARY_DIR}/${SOURCE}.${TARGET}.cc)
if(NOT EXISTS ${PATH})
file(WRITE ${PATH} "#define MOLD_${TARGET} 1
#define MOLD_TARGET ${TARGET}
#include \"${CMAKE_SOURCE_DIR}/${SOURCE}\"
")
endif()
target_sources(mold PRIVATE ${PATH})
endfunction()
foreach (SOURCE IN LISTS MOLD_ELF_TEMPLATE_FILES)
foreach(TARGET IN LISTS MOLD_ELF_TARGETS)
mold_instantiate_templates(${SOURCE} ${TARGET})
endforeach()
endforeach()
# Add other non-template source files.
target_sources(mold PRIVATE
common/compress.cc
common/demangle.cc
common/filepath.cc
common/glob.cc
common/hyperloglog.cc
common/main.cc
common/multi-glob.cc
common/perf.cc
common/tar.cc
common/uuid.cc
elf/arch-alpha.cc
elf/arch-arm32.cc
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-m68k.cc
elf/arch-ppc32.cc
elf/arch-ppc64v1.cc
elf/arch-ppc64v2.cc
elf/arch-s390x.cc
elf/arch-sh4.cc
elf/arch-sparc64.cc
elf/arch-x86-64.cc
elf/elf.cc
git-hash.cc
third-party/rust-demangle/rust-demangle.c
)
# Add frequently included header files for pre-compiling.
# target_precompile_headers is supported by CMake 3.16.0 or newer.
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
# ccache needs this flag along with `sloppiness = pch_defines,time_macros`
# to enable caching
if(NOT MSVC)
target_compile_options(mold PRIVATE -fpch-preprocess)
endif()
endif()
include(CTest)
if(BUILD_TESTING)
# Create the ld and ld64 symlinks required for testing
if(NOT WIN32)
add_custom_command(
TARGET mold POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink mold ld
BYPRODUCTS ld
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM)
endif()
if(${UNIX})
add_subdirectory(test/elf)
endif()
endif()
if(NOT CMAKE_SKIP_INSTALL_RULES)
install(TARGETS mold RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES docs/mold.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)
if(EXISTS "${CMAKE_SOURCE_DIR}/LICENSE")
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
endif()
if(EXISTS "${CMAKE_SOURCE_DIR}/LICENSE.third-party")
install(FILES "LICENSE.third-party" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
endif()
function(mold_install_relative_symlink OLD NEW)
install(CODE "
get_filename_component(PREFIX_ABS \${CMAKE_INSTALL_PREFIX}/ ABSOLUTE)
get_filename_component(OLD_ABS ${OLD} ABSOLUTE BASE_DIR \${PREFIX_ABS})
get_filename_component(NEW_ABS ${NEW} ABSOLUTE BASE_DIR \${PREFIX_ABS})
get_filename_component(NEW_DIR \${NEW_ABS} DIRECTORY)
file(RELATIVE_PATH OLD_REL \${NEW_DIR} \${OLD_ABS})
message(STATUS \"Installing symlink: \$ENV{DESTDIR}\${NEW_ABS} -> \${OLD_REL}\")
file(MAKE_DIRECTORY \$ENV{DESTDIR}\${NEW_DIR})
file(CREATE_LINK \${OLD_REL} \$ENV{DESTDIR}\${NEW_ABS} SYMBOLIC)")
endfunction()
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold${CMAKE_EXECUTABLE_SUFFIX}
${CMAKE_INSTALL_LIBEXECDIR}/mold/ld${CMAKE_EXECUTABLE_SUFFIX})
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold${CMAKE_EXECUTABLE_SUFFIX}
${CMAKE_INSTALL_BINDIR}/ld.mold${CMAKE_EXECUTABLE_SUFFIX})
mold_install_relative_symlink(${CMAKE_INSTALL_MANDIR}/man1/mold.1
${CMAKE_INSTALL_MANDIR}/man1/ld.mold.1)
endif()
Generated
+590
View File
@@ -0,0 +1,590 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "arrayvec"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "blake3"
version = "1.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae"
dependencies = [
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures",
]
[[package]]
name = "bstr"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
dependencies = [
"memchr",
"regex-automata",
"serde_core",
]
[[package]]
name = "cc"
version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "cpp_demangle"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253"
dependencies = [
"cfg-if",
]
[[package]]
name = "cpufeatures"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
[[package]]
name = "either"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
[[package]]
name = "flate2"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
"libz-sys",
]
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "jobserver"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libmimalloc-sys"
version = "0.1.49"
source = "git+https://github.com/rui314/mimalloc_rust?rev=3979460494f1cd1e7f936cb8e10f41e927c9f698#3979460494f1cd1e7f936cb8e10f41e927c9f698"
dependencies = [
"cc",
]
[[package]]
name = "libz-sys"
version = "1.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "memmap2"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
dependencies = [
"libc",
]
[[package]]
name = "mimalloc"
version = "0.1.52"
source = "git+https://github.com/rui314/mimalloc_rust?rev=3979460494f1cd1e7f936cb8e10f41e927c9f698#3979460494f1cd1e7f936cb8e10f41e927c9f698"
dependencies = [
"libmimalloc-sys",
]
[[package]]
name = "mold"
version = "3.0.0"
dependencies = [
"blake3",
"bstr",
"cc",
"cpp_demangle",
"crc32fast",
"flate2",
"getrandom",
"hashbrown",
"libc",
"libz-sys",
"memchr",
"memmap2",
"portable-atomic",
"rayon",
"rustc-demangle",
"uuid",
"xxhash-rust",
"zstd",
]
[[package]]
name = "mold-cli"
version = "3.0.0"
dependencies = [
"mimalloc",
"mold",
"mold-target-arm32",
"mold-target-arm32be",
"mold-target-arm64",
"mold-target-arm64be",
"mold-target-i386",
"mold-target-loongarch32",
"mold-target-loongarch64",
"mold-target-m68k",
"mold-target-ppc32",
"mold-target-ppc64v1",
"mold-target-ppc64v2",
"mold-target-riscv32",
"mold-target-riscv32be",
"mold-target-riscv64",
"mold-target-riscv64be",
"mold-target-s390x",
"mold-target-sh4",
"mold-target-sh4be",
"mold-target-sparc64",
"mold-target-x86_64",
"mold-tests",
]
[[package]]
name = "mold-target-arm32"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-arm32be"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-arm64"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-arm64be"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-i386"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-loongarch32"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-loongarch64"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-m68k"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-ppc32"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-ppc64v1"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-ppc64v2"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-riscv32"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-riscv32be"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-riscv64"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-riscv64be"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-s390x"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-sh4"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-sh4be"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-sparc64"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-target-x86_64"
version = "3.0.0"
dependencies = [
"mold",
]
[[package]]
name = "mold-tests"
version = "3.0.0"
dependencies = [
"libc",
]
[[package]]
name = "pkg-config"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]]
name = "portable-atomic"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rayon"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
[[package]]
name = "rustc-demangle"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb"
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "syn"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "uuid"
version = "1.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97277d36b9c3ace13e58fa6e753f8b0bbbf302a18dd193240d70f9e29681059a"
dependencies = [
"getrandom",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "xxhash-rust"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6"
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.16+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
dependencies = [
"cc",
"pkg-config",
]
+53
View File
@@ -0,0 +1,53 @@
[package]
name = "mold"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "A high-performance ELF linker, rewritten in Rust"
license.workspace = true
# tests/lib.rs belongs to the mold-tests workspace crate.
autotests = false
# The linker is generic over the target. Instantiating it for all twenty
# targets in one crate keeps the compiler on a single core for minutes, so
# each target is instantiated in a crate of its own under arch/, and the
# executable in cli/ dispatches to them.
[workspace]
members = ["cli", "arch/*", "tests"]
default-members = [".", "cli", "arch/*", "tests"]
[workspace.package]
version = "3.0.0"
edition = "2024"
rust-version = "1.95"
license = "MIT"
[workspace.dependencies]
libc = "0.2"
mold = { path = "." }
[dependencies]
blake3 = "1.8.7"
bstr = "1"
cpp_demangle = "0.4"
crc32fast = "1"
flate2 = { version = "1", default-features = false, features = ["zlib"] }
getrandom = "0.4.3"
hashbrown = "0.17.1"
libc.workspace = true
libz-sys = "1.1.29"
memchr = "2.8.3"
memmap2 = "0.9"
# std has no 64-bit atomics on hosts such as 32-bit PowerPC.
portable-atomic = "1"
rayon = "1"
rustc-demangle = "0.1"
uuid = { version = "1.27", default-features = false, features = ["v4"] }
xxhash-rust = { version = "0.8", features = ["xxh3"] }
zstd = "0.13"
[build-dependencies]
cc = "1"
[profile.release]
panic = "abort"
-348
View File
@@ -1,348 +0,0 @@
Mold includes a number of subcomponents with separate copyright notices
and license terms. Your use of the source code for the these subcomponents
is subject to the terms and conditions of the following licenses.
=== For the third-party/mimalloc component:
MIT License
Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=== For the third-party/rust-demangle component:
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=== For the third-party/tbb component:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=== For the third-party/xxhash component:
xxHash Library
Copyright (c) 2012-2020 Yann Collet
All rights reserved.
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=== For the third-party/zlib component:
(C) 1995-2022 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
[email protected] [email protected]
=== For the third-party/zstd component:
BSD License
For Zstandard software
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+164 -88
View File
@@ -1,31 +1,21 @@
# mold: A Modern Linker
mold is a faster drop-in replacement for existing Unix linkers. It is several
times quicker than the LLVM lld linker, the second-fastest open-source linker,
which I initially developed a few years ago. mold aims to enhance developer
productivity by minimizing build time, particularly in rapid
debug-edit-rebuild cycles.
mold is a high-performance drop-in replacement for existing Unix linkers,
designed to speed up builds. In our August 2026 benchmarks, it links 4.9x
faster than [LLVM lld](https://lld.llvm.org) and 1.9x faster than
[wild](https://github.com/wild-linker/wild) at the median; see
[Benchmark](#benchmark) for the full results.
Here is a performance comparison of GNU ld, GNU gold, LLVM lld, and
mold when linking final debuginfo-enabled executables for major large
programs on a simulated 16-core, 32-thread machine.
mold is written by the original developer of LLVM lld, the linker that
Android, Chrome, FreeBSD, PlayStation, Nintendo Switch, and other production
systems are built with. mold started as an effort to build an even faster
linker from scratch, free of the architectural limits its author had run into
while optimizing lld. It has been in production use since 2021, and today it
is the default linker of many large open-source projects and is used
internally by many companies.
![Link speed comparison](docs/chart.svg)
| Program (linker output size) | GNU ld | GNU gold | LLVM lld | mold
|-------------------------------|--------|----------|----------|------
| MySQL 8.3 (0.47 GiB) | 10.84s | 7.47s | 1.64s | 0.46s
| Clang 19 (1.56 GiB) | 42.07s | 33.13s | 5.20s | 1.35s
| Chromium 124 (1.35 GiB) | N/A | 27.40s | 6.10s | 1.52s
mold is so fast that it is only 2x _slower_ than the `cp` command on the same
machine. If you find that mold is not faster than other linkers, feel
free to [file a bug report](https://github.com/rui314/mold/issues).
mold supports x86-64, i386, ARM64, ARM32, 64-bit/32-bit little/big-endian
RISC-V, 32-bit PowerPC, 64-bit big-endian PowerPC ELFv1, 64-bit little-endian
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, SH-4, and DEC
Alpha.
mold supports x86-64, i386, ARM 32/64, RISC-V 32/64, PowerPC 32/64, s390x,
LoongArch 32/64, SPARC64, m68k, and SH-4.
## Why does linking speed matter?
@@ -39,52 +29,134 @@ speed up this process, saving you time and preventing distractions while
waiting for a lengthy build to finish. The difference is most noticeable
during rapid debug-edit-rebuild cycles.
## Benchmark
Here is a performance comparison of lld, wild, and mold when linking nine
large programs on two machines:
- AMD Ryzen Threadripper 7980X (64 cores) running Ubuntu 24.04
- Apple M1 Ultra (16 performance cores and 4 efficiency cores) running Fedora
Asahi Remix 42; the benchmark is restricted to the performance cores
The Threadripper represents many-core workstation and server processors, and
the M1 Ultra represents high-performance desktop processors.
All three linkers were built from source in release configuration as of
2026-08-28 and ran with their default options. Times are wall-clock times as
seen by the caller, the median of three runs after a warm-up. The ARM64 rows
are the ARM64 builds of the same programs. The benchmark suite, including all
linker inputs, is available on [Zenodo](https://zenodo.org/records/21882261).
**AMD Ryzen Threadripper 7980X, debug builds**
| Program (output size) | lld | wild | mold | wild/mold | lld/mold
|-----------------------------------|--------|-------|-------|-----------|---------
| Blender 5.2 (2.46 GiB) | 4.72s | 1.98s | 0.86s | 2.3x | 5.5x
| Chromium 145 (4.51 GiB) | 16.64s | 3.98s | 1.65s | 2.4x | 10.1x
| Chromium 145 ARM64 (4.76 GiB) | 20.91s | N/A | 1.83s | N/A | 11.4x
| Clang 21 (4.19 GiB) | 6.19s | 3.71s | 1.34s | 2.8x | 4.6x
| ClickHouse 26.1 (5.58 GiB) | 6.62s | 4.40s | 0.98s | 4.5x | 6.7x
| Firefox 149 (2.37 GiB) | 5.11s | N/A | 0.78s | N/A | 6.5x
| Firefox 149 ARM64 (2.43 GiB) | 6.40s | N/A | 0.93s | N/A | 6.9x
| Godot 4.6 (1.01 GiB) | 1.77s | 1.08s | 0.46s | 2.3x | 3.8x
| LibreOffice 26.2 (0.98 GiB) | 3.46s | 1.41s | 0.44s | 3.2x | 7.9x
| PyTorch 2.9 (3.51 GiB) | 4.35s | 2.48s | 0.80s | 3.1x | 5.4x
| TensorFlow 2.21 (9.55 GiB) | 50.73s | N/A | 3.15s | N/A | 16.1x
**AMD Ryzen Threadripper 7980X, release builds**
| Program (output size) | lld | wild | mold | wild/mold | lld/mold
|-----------------------------------|-------|-------|-------|-----------|---------
| Blender 5.2 (0.24 GiB) | 0.85s | 0.30s | 0.20s | 1.5x | 4.2x
| Chromium 145 (0.58 GiB) | 6.48s | N/A | 0.64s | N/A | 10.2x
| Chromium 145 ARM64 (0.60 GiB) | 7.91s | N/A | 0.73s | N/A | 10.8x
| Clang 21 (0.21 GiB) | 0.53s | 0.23s | 0.11s | 2.2x | 5.0x
| ClickHouse 26.1 (1.23 GiB) | 3.18s | 2.07s | 0.41s | 5.0x | 7.7x
| Firefox 149 (0.22 GiB) | 1.01s | 0.41s | 0.21s | 2.0x | 4.9x
| Godot 4.6 (0.15 GiB) | 0.44s | 0.21s | 0.08s | 2.7x | 5.8x
| LibreOffice 26.2 (0.19 GiB) | 1.13s | 0.57s | 0.19s | 3.0x | 6.0x
| PyTorch 2.9 (0.31 GiB) | 0.68s | 0.32s | 0.15s | 2.2x | 4.7x
| TensorFlow 2.21 (0.73 GiB) | 9.62s | N/A | 0.70s | N/A | 13.7x
**Apple M1 Ultra, debug builds**
| Program (output size) | lld | wild | mold | wild/mold | lld/mold
|-----------------------------------|--------|-------|-------|-----------|---------
| Blender 5.2 (2.46 GiB) | 3.21s | 1.81s | 1.56s | 1.2x | 2.1x
| Chromium 145 (4.51 GiB) | 9.54s | 3.49s | 2.22s | 1.6x | 4.3x
| Chromium 145 ARM64 (4.76 GiB) | 12.67s | N/A | 2.31s | N/A | 5.5x
| Clang 21 (4.19 GiB) | 4.40s | 2.78s | 2.96s | 0.9x | 1.5x
| ClickHouse 26.1 (5.58 GiB) | 4.92s | 3.50s | 1.71s | 2.1x | 2.9x
| Firefox 149 (2.37 GiB) | 3.21s | N/A | 1.12s | N/A | 2.9x
| Firefox 149 ARM64 (2.43 GiB) | 4.13s | N/A | 1.12s | N/A | 3.7x
| Godot 4.6 (1.01 GiB) | 1.12s | 0.81s | 0.62s | 1.3x | 1.8x
| LibreOffice 26.2 (0.98 GiB) | 2.08s | 0.94s | 0.63s | 1.5x | 3.3x
| PyTorch 2.9 (3.51 GiB) | 3.07s | 2.10s | 1.45s | 1.4x | 2.1x
| TensorFlow 2.21 (9.55 GiB) | 43.68s | N/A | 4.43s | N/A | 9.9x
**Apple M1 Ultra, release builds**
| Program (output size) | lld | wild | mold | wild/mold | lld/mold
|-----------------------------------|-------|-------|-------|-----------|---------
| Blender 5.2 (0.24 GiB) | 0.56s | 0.20s | 0.25s | 0.8x | 2.3x
| Chromium 145 (0.58 GiB) | 4.25s | N/A | 0.78s | N/A | 5.5x
| Chromium 145 ARM64 (0.60 GiB) | 5.17s | N/A | 0.91s | N/A | 5.7x
| Clang 21 (0.21 GiB) | 0.30s | 0.15s | 0.14s | 1.0x | 2.1x
| ClickHouse 26.1 (1.23 GiB) | 1.94s | 0.95s | 0.55s | 1.7x | 3.5x
| Firefox 149 (0.22 GiB) | 0.56s | 0.23s | 0.20s | 1.1x | 2.7x
| Godot 4.6 (0.15 GiB) | 0.26s | 0.11s | 0.11s | 1.0x | 2.3x
| LibreOffice 26.2 (0.19 GiB) | 0.62s | 0.30s | 0.23s | 1.3x | 2.7x
| PyTorch 2.9 (0.31 GiB) | 0.43s | 0.22s | 0.17s | 1.3x | 2.5x
| TensorFlow 2.21 (0.73 GiB) | 8.07s | N/A | 0.68s | N/A | 11.9x
N/A indicates that the linker cannot link that program. wild's Chromium
release links are also marked N/A because wild does not implement `--icf=all`
and links without identical code folding.
## Why is mold so fast?
mold owes its speed to pervasive parallelism and to efficient data structures
and algorithms. For details, read our paper "mold: A Massively Parallel
Linker" (ASPLOS 2027), available at https://arxiv.org/abs/2608.23228.
## Installation
Binary packages for the following systems are currently available:
[![Packaging status](https://repology.org/badge/vertical-allrepos/mold.svg)](https://repology.org/project/mold/versions)
Prebuilt binaries for Linux on x86-64, ARM64, ARM32, RISC-V, PPC64LE, s390x,
and LoongArch are also attached to each
[GitHub release](https://github.com/rui314/mold/releases).
## How to Build
mold is written in C++20, so if you build mold yourself, you will need a
recent version of a C++ compiler and a C++ standard library. We recommend GCC
10.2 or Clang 12.0.0 (or later) and libstdc++ 10 or libc++ 7 (or later).
### Install Dependencies
To install build dependencies, run `./install-build-deps.sh` in this
directory. It will detect your Linux distribution and attempt to install the
necessary packages. You may need to run it as root.
mold is written in Rust and built with Cargo. It requires Rust 1.95 or later;
if your system's Rust is older, install the stable toolchain with
[rustup](https://rustup.rs/). You also need Git and a C compiler such as GCC or
Clang, which you can install with your system's package manager.
### Compile mold
```shell
git clone https://github.com/rui314/mold.git
mkdir mold/build
cd mold/build
git checkout stable
../install-build-deps.sh
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
cmake --build . -j $(nproc)
sudo cmake --build . --target install
cd mold
cargo build --release
```
You might need to pass a C++20 compiler command name to `cmake`. In the
example above, `c++` is passed. If that doesn't work for you, try a specific
version of a compiler, such as `g++-10` or `clang++-12`.
The executable and its companion preload library are
`target/release/mold` and `target/release/mold-wrapper.so`.
By default, `mold` is installed to `/usr/local/bin`. You can change the
installation location by passing `-DCMAKE_INSTALL_PREFIX=<directory>`.
For other cmake options, see the comments in `CMakeLists.txt`.
### Install mold
If you are not using a recent enough Linux distribution, or if `cmake` does
not work for you for any reason, you can use Docker to build mold in a Docker
environment. To do so, run `./dist.sh` in this directory instead of using
`cmake`. The shell script will pull a Docker image, build mold and auxiliary
files inside it, and package them into a single tar file named
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere and
use the mold executable within it.
Run `sudo ./install-mold.sh` to install mold under `/usr/local`. To install it
under a different prefix, set `PREFIX`, as in
`sudo PREFIX=/usr ./install-mold.sh`. `mold-wrapper.so` is installed to
`$PREFIX/lib/mold`. If your system uses another library directory, such as
`/usr/lib64`, set `MOLD_LIBDIR` to it for both `cargo build` and
`install-mold.sh`.
You can also run `target/release/mold` directly without installing it.
## How to use
@@ -92,7 +164,7 @@ use the mold executable within it.
On Unix, the linker command (usually `/usr/bin/ld`) is indirectly invoked by
the compiler driver (typically `cc`, `gcc`, or `clang`), which is in turn
indirectly invoked by `make` or another build system command.
indirectly invoked by `make` or other build system commands.
If you can specify an additional command line option for your compiler driver
by modifying the build system's config files, add one of the following flags
@@ -106,11 +178,10 @@ to use mold instead of `/usr/bin/ld`:
valid argument, so you need to use the `-B` option instead. The `-B` option
tells GCC where to look for external commands like `ld`.
If you have installed mold with `make install`, there should be a directory
named `/usr/libexec/mold` (or `/usr/local/libexec/mold`, depending on your
`$PREFIX`), and the `ld` command should be there. The `ld` is actually a
symlink to `mold`. So, all you need is to pass `-B/usr/libexec/mold` (or
`-B/usr/local/libexec/mold`) to GCC.
If you installed mold using the script above, there is a directory named
`/usr/local/libexec/mold`, and the `ld` command there is a symlink to mold.
Pass `-B/usr/local/libexec/mold` to GCC. If you installed under another
prefix, use that prefix instead.
If you haven't installed `ld.mold` to any `$PATH`, you can still pass
`-fuse-ld=/absolute/path/to/mold` to clang to use mold. However, GCC does not
@@ -123,7 +194,7 @@ accept an absolute path as an argument for `-fuse-ld`.
Create `.cargo/config.toml` in your project directory with the following:
```toml
[target.x86_64-unknown-linux-gnu]
[target.'cfg(target_os = "linux")']
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
@@ -134,8 +205,8 @@ example above, we use `clang` as a linker driver since it always accepts the
may be able to remove the `linker = "clang"` line.
```toml
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
```
If you want to use mold for all projects, add the above snippet to
@@ -161,22 +232,30 @@ If you want to use mold for all projects, add the above snippet to
</details>
<details><summary>If you are using Conan package manager</summary>
You can configure [Conan](https://github.com/conan-io) to download the latest
version of `mold` and use it as the linker when building your dependencies and
projects from source. Please see the instructions [here](https://conan.io/center/recipes/mold).
</details>
<details><summary>mold -run</summary>
It is sometimes very hard to pass an appropriate command line option to `cc`
to specify an alternative linker. To address this situation, mold has a
feature to intercept all invocations of `ld`, `ld.lld`, or `ld.gold` and
redirect them to itself. To use this feature, run `make` (or another build
feature to intercept all invocations of `ld`, `ld.bfd`, `ld.lld`, or `ld.gold`
and redirect them to itself. To use this feature, run `make` (or another build
command) as a subcommand of mold as follows:
```shell
mold -run make <make-options-if-any>
```
Internally, mold invokes the given command with the `LD_PRELOAD` environment
Internally, mold invokes a given command with the `LD_PRELOAD` environment
variable set to its companion shared object file. The shared object file
intercepts all function calls to `exec(3)`-family functions to replace
`argv[0]` with `mold` if it is `ld`, `ld.gold`, or `ld.lld`.
`argv[0]` with `mold` if it is `ld`, `ld.bfd`, `ld.gold`, or `ld.lld`.
</details>
@@ -184,7 +263,7 @@ intercepts all function calls to `exec(3)`-family functions to replace
You can use our [setup-mold](https://github.com/rui314/setup-mold) GitHub
Action to speed up GitHub-hosted continuous builds. Although GitHub Actions
run on a two-core machine, mold is still significantly faster than the default
run on a 4 core machine, mold is still significantly faster than the default
GNU linker, especially when linking large programs.
</details>
@@ -210,34 +289,30 @@ If `mold` is present in the `.comment` section, the file was created by mold.
Since mold is a drop-in replacement, you should be able to use it without
reading its manual. However, if you need it, [mold's man page](docs/mold.md)
is available. You can read the same manual by running `man mold`.
is available online. You can read the same manual by running `man mold`.
</details>
## Why is mold so fast?
## Stability
One reason is that it utilizes faster algorithms and more efficient data
structures compared to other linkers. Another reason is that mold is highly
parallelized.
Here is a side-by-side comparison of per-core CPU usage for lld (left) and
mold (right), linking the same program, a Chromium executable.
![CPU usage comparison in htop animation](docs/htop.gif)
As you can see, mold uses all available cores throughout its execution and
finishes quickly. In contrast, lld fails to utilize available cores most of
the time. In this demo, the maximum parallelism is artificially capped at 16,
so that the bars fit in the GIF.
For details, please see the [design notes](docs/design.md).
mold has been developed in the open since 2020 and has more than 140
contributors. Its test suite, which covers every linker feature, runs in CI for
all supported target CPU architectures, natively or under QEMU, and under ASAN
and TSAN. Before each release, we try to build all of Gentoo Linux's roughly
19,000 packages with mold, using GNU ld as a control, to find regressions before
they reach a release.
## Sponsors
We accept donations via [GitHub Sponsors](https://github.com/sponsors/rui314)
and [OpenCollective](https://opencollective.com/mold-linker). We thank
everyone who sponsors our project. In particular, we'd like to acknowledge the
following people and organizations who have sponsored $128/month or more:
mold is free to use, but keeping it maintained is continuous work: supporting
new architectures and toolchain features, keeping up with the projects that
depend on it, and making it faster. That work is funded by sponsors. If mold
saves you or your company time, please consider becoming a
[GitHub sponsor](https://github.com/sponsors/rui314).
We thank everyone who sponsors the project. In particular, we'd like to
acknowledge the following people and organizations who have sponsored
$128/month or more:
### Corporate sponsors
@@ -258,3 +333,4 @@ following people and organizations who have sponsored $128/month or more:
- [Bryant Biggs](https://github.com/bryantbiggs)
- [kraptor23](https://github.com/kraptor23)
- [Jinkyu Yi](https://github.com/jincreator)
- [Pedro Navarro](https://github.com/pedronavf)
+11
View File
@@ -0,0 +1,11 @@
# Architecture crates
Each subdirectory is a small crate that instantiates the generic linker for
one target architecture. Separate crates let Cargo compile the architectures
in parallel. Instantiating all targets in one crate leaves a long serial
compilation step.
The shared linker implementation lives in [`src/`](../src/), with
target-specific code in [`src/target/`](../src/target/). These crates provide
entry points that the [`cli/`](../cli/) executable selects for the input
files' target.
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-arm32"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for arm32"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Arm32>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-arm32be"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for arm32be"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Arm32Be>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-arm64"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for arm64"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Arm64>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-arm64be"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for arm64be"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Arm64Be>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-i386"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for i386"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::I386>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-loongarch32"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for loongarch32"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::LoongArch32>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-loongarch64"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for loongarch64"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::LoongArch64>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-m68k"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for m68k"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::M68k>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-ppc32"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for ppc32"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Ppc32>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-ppc64v1"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for ppc64v1"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Ppc64V1>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-ppc64v2"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for ppc64v2"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Ppc64V2>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-riscv32"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for riscv32"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Riscv32>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-riscv32be"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for riscv32be"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Riscv32Be>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-riscv64"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for riscv64"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Riscv64>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-riscv64be"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for riscv64be"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Riscv64Be>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-s390x"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for s390x"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::S390x>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-sh4"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for sh4"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Sh4>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-sh4be"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for sh4be"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Sh4Be>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-sparc64"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for sparc64"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::Sparc64>(cmdline)
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "mold-target-x86_64"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold linker instantiated for x86_64"
license.workspace = true
[lib]
path = "lib.rs"
[dependencies]
mold.workspace = true
+3
View File
@@ -0,0 +1,3 @@
pub fn link(cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
mold::driver::link::<mold::target::X86_64>(cmdline)
}
+115
View File
@@ -0,0 +1,115 @@
//! Builds the two small C parts of mold.
//!
//! `mold-wrapper.so` is the preload library behind `mold -run`. It
//! interposes the exec family, including the C-variadic `execl` functions
//! that stable Rust cannot define, so it is a C file. It is placed next to
//! the `mold` executable, where `-run` looks for it.
//!
//! `lto-message.c` adapts the LTO plugin's printf-like diagnostics
//! callback, which is C-variadic as well, and is linked into mold.
use std::path::{Path, PathBuf};
use std::process::Command;
// Embed the current git commit hash into the mold executable. We ask git
// instead of parsing files under .git because the repository format varies
// (e.g. packed refs, reftable). When building from a source tarball, there's
// no .git, and the hash is simply omitted.
fn git_hash(source_dir: &Path) -> Option<String> {
let dot_git = source_dir.join(".git");
if !dot_git.exists() {
// Source archives use the package version. If Git metadata is added
// later, a clean rebuild is needed to embed the new commit hash.
return None;
}
if dot_git.is_file() {
println!("cargo:rerun-if-changed={}", dot_git.display());
}
let git_path = |name: &str| -> Option<PathBuf> {
let output = Command::new("git")
.arg("-C")
.arg(source_dir)
.args(["rev-parse", "--git-path", name])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let path = PathBuf::from(String::from_utf8(output.stdout).ok()?.trim());
Some(if path.is_absolute() { path } else { source_dir.join(path) })
};
let reftable = git_path("reftable/tables.list").filter(|p| p.exists());
if let Some(head) = git_path("HEAD").filter(|p| p.exists()) {
println!("cargo:rerun-if-changed={}", head.display());
if let Ok(contents) = std::fs::read_to_string(&head)
&& let Some(reference) =
contents.strip_prefix("ref: ").map(str::trim).filter(|_| reftable.is_none())
&& let Some(path) = git_path(reference)
{
// A packed reference may acquire a loose file later.
// Watch its nearest existing directory until that happens.
if let Some(path) = path.ancestors().find(|p| p.exists()) {
println!("cargo:rerun-if-changed={}", path.display());
}
}
}
for path in git_path("packed-refs").into_iter().chain(reftable).filter(|p| p.exists()) {
println!("cargo:rerun-if-changed={}", path.display());
}
let output =
Command::new("git").arg("-C").arg(source_dir).args(["rev-parse", "HEAD"]).output().ok()?;
output.status.success().then(|| String::from_utf8_lossy(&output.stdout).trim().to_string())
}
fn main() {
let source_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let version = std::env::var("CARGO_PKG_VERSION").unwrap();
let version = match git_hash(&source_dir) {
Some(hash) => format!("mold {version} ({hash}; compatible with GNU ld)"),
None => format!("mold {version} (compatible with GNU ld)"),
};
println!("cargo:rustc-env=MOLD_VERSION={version}");
println!("cargo:rerun-if-changed=c/mold-wrapper.c");
println!("cargo:rerun-if-changed=c/lto-message.c");
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap();
if target_os == "windows" && target_env == "msvc" {
return;
}
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let mut build = cc::Build::new();
build.opt_level(2).pic(true);
if let Ok(sanitizer) = std::env::var("CARGO_CFG_SANITIZE") {
build.flag(format!("-fsanitize={sanitizer}"));
}
build.file("c/lto-message.c").compile("ltomessage");
if target_os == "windows" || target_os == "macos" {
return;
}
// The build directory is inside the actual profile directory. PROFILE
// only describes inheritance and need not be that directory's name.
let profile_dir = out_dir
.ancestors()
.find(|path| path.file_name().is_some_and(|name| name == "build"))
.and_then(Path::parent)
.expect("OUT_DIR must be inside Cargo's build directory");
let wrapper = profile_dir.join("mold-wrapper.so");
let mut command = build.get_compiler().to_command();
command.args(["-shared", "-o"]);
command.arg(&wrapper).arg("c/mold-wrapper.c");
if target_os == "android" || target_os == "linux" {
command.arg("-ldl");
}
let status = command.status();
if !matches!(status, Ok(s) if s.success()) {
println!("cargo:warning=could not build mold-wrapper.so; `mold -run` will not work");
}
}
+18
View File
@@ -0,0 +1,18 @@
// The linker plugin reports diagnostics through a printf-like callback.
// C-variadic functions can't be defined in stable Rust, so this adapter
// formats the message and hands it to Rust.
#include <stdarg.h>
#include <stdio.h>
void mold_lto_report(int level, const char *msg);
int mold_lto_message(int level, const char *fmt, ...) {
char buf[1000];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
mold_lto_report(level, buf);
return 0;
}
+4 -3
View File
@@ -1,8 +1,5 @@
#define _GNU_SOURCE 1
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
# include <alloca.h>
#endif
#include <dlfcn.h>
#include <spawn.h>
#include <stdarg.h>
@@ -12,6 +9,10 @@
#include <string.h>
#include <unistd.h>
#if __has_include(<alloca.h>)
# include <alloca.h>
#endif
extern char **environ;
static char *get_mold_path() {
+72
View File
@@ -0,0 +1,72 @@
[package]
name = "mold-cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "The mold executable"
license.workspace = true
[[bin]]
name = "mold"
path = "main.rs"
[[test]]
name = "integration"
path = "integration.rs"
harness = false
[dependencies]
mimalloc = { git = "https://github.com/rui314/mimalloc_rust", rev = "3979460494f1cd1e7f936cb8e10f41e927c9f698" }
mold.workspace = true
mold-target-x86_64 = { path = "../arch/x86_64", optional = true }
mold-target-i386 = { path = "../arch/i386", optional = true }
mold-target-arm32 = { path = "../arch/arm32", optional = true }
mold-target-arm32be = { path = "../arch/arm32be", optional = true }
mold-target-arm64 = { path = "../arch/arm64", optional = true }
mold-target-arm64be = { path = "../arch/arm64be", optional = true }
mold-target-riscv64 = { path = "../arch/riscv64", optional = true }
mold-target-riscv64be = { path = "../arch/riscv64be", optional = true }
mold-target-riscv32 = { path = "../arch/riscv32", optional = true }
mold-target-riscv32be = { path = "../arch/riscv32be", optional = true }
mold-target-ppc64v2 = { path = "../arch/ppc64v2", optional = true }
mold-target-ppc32 = { path = "../arch/ppc32", optional = true }
mold-target-ppc64v1 = { path = "../arch/ppc64v1", optional = true }
mold-target-s390x = { path = "../arch/s390x", optional = true }
mold-target-sparc64 = { path = "../arch/sparc64", optional = true }
mold-target-m68k = { path = "../arch/m68k", optional = true }
mold-target-sh4 = { path = "../arch/sh4", optional = true }
mold-target-sh4be = { path = "../arch/sh4be", optional = true }
mold-target-loongarch64 = { path = "../arch/loongarch64", optional = true }
mold-target-loongarch32 = { path = "../arch/loongarch32", optional = true }
[dev-dependencies]
mold-tests = { path = "../tests" }
# One feature per target. All are built by default; a build for one
# target alone (`--no-default-features --features x86_64`) is much faster.
[features]
default = [
"x86_64", "i386", "arm32", "arm32be", "arm64", "arm64be", "riscv64", "riscv64be", "riscv32", "riscv32be",
"ppc64v2", "ppc32", "ppc64v1", "s390x", "sparc64", "m68k", "sh4", "sh4be", "loongarch64", "loongarch32",
]
system-allocator = []
x86_64 = ["dep:mold-target-x86_64"]
i386 = ["dep:mold-target-i386"]
arm32 = ["dep:mold-target-arm32"]
arm32be = ["dep:mold-target-arm32be"]
arm64 = ["dep:mold-target-arm64"]
arm64be = ["dep:mold-target-arm64be"]
riscv64 = ["dep:mold-target-riscv64"]
riscv64be = ["dep:mold-target-riscv64be"]
riscv32 = ["dep:mold-target-riscv32"]
riscv32be = ["dep:mold-target-riscv32be"]
ppc64v2 = ["dep:mold-target-ppc64v2"]
ppc32 = ["dep:mold-target-ppc32"]
ppc64v1 = ["dep:mold-target-ppc64v1"]
s390x = ["dep:mold-target-s390x"]
sparc64 = ["dep:mold-target-sparc64"]
m68k = ["dep:mold-target-m68k"]
sh4 = ["dep:mold-target-sh4"]
sh4be = ["dep:mold-target-sh4be"]
loongarch64 = ["dep:mold-target-loongarch64"]
loongarch32 = ["dep:mold-target-loongarch32"]
+15
View File
@@ -0,0 +1,15 @@
//! Links libatomic into mold on hosts without 64-bit atomics, such as
//! 32-bit PowerPC. mimalloc uses 64-bit atomics, which C compilers
//! implement as calls into libatomic on those hosts.
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let widths = std::env::var("CARGO_CFG_TARGET_HAS_ATOMIC").unwrap_or_default();
if !widths.split(',').any(|w| w == "64") {
// rustc places libraries named by this crate before its dependencies
// on the linker command line, where --as-needed would drop libatomic.
// Link arguments come after all libraries.
println!("cargo:rustc-link-arg-bins=-latomic");
}
}
+8
View File
@@ -0,0 +1,8 @@
use std::path::Path;
use std::process::ExitCode;
fn main() -> ExitCode {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
let cases = [root.join("tests")];
mold_tests::run(&cases, Path::new(env!("CARGO_BIN_EXE_mold")))
}
+79
View File
@@ -0,0 +1,79 @@
//! The `mold` executable: runs the linker instantiated for the target the
//! inputs are for. Each target is instantiated in a crate of its own so
//! that the compiler can build them in parallel, and a feature per target
//! decides which of them are built in.
// A Rust executable can define only one global allocator, so select mimalloc
// here rather than in the linker library.
#[cfg(not(feature = "system-allocator"))]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
type LinkFn = fn(mold::driver::Cmdline) -> mold::driver::LinkResult;
// Each target has its own monomorphized link function. Start with the first
// enabled target and switch to the matching function if the inputs differ.
const TARGETS: &[(&str, LinkFn)] = &[
#[cfg(feature = "x86_64")]
("x86_64", mold_target_x86_64::link),
#[cfg(feature = "i386")]
("i386", mold_target_i386::link),
#[cfg(feature = "arm32")]
("arm32", mold_target_arm32::link),
#[cfg(feature = "arm32be")]
("arm32be", mold_target_arm32be::link),
#[cfg(feature = "arm64")]
("arm64", mold_target_arm64::link),
#[cfg(feature = "arm64be")]
("arm64be", mold_target_arm64be::link),
#[cfg(feature = "riscv64")]
("riscv64", mold_target_riscv64::link),
#[cfg(feature = "riscv64be")]
("riscv64be", mold_target_riscv64be::link),
#[cfg(feature = "riscv32")]
("riscv32", mold_target_riscv32::link),
#[cfg(feature = "riscv32be")]
("riscv32be", mold_target_riscv32be::link),
#[cfg(feature = "ppc64v2")]
("ppc64v2", mold_target_ppc64v2::link),
#[cfg(feature = "ppc32")]
("ppc32", mold_target_ppc32::link),
#[cfg(feature = "ppc64v1")]
("ppc64v1", mold_target_ppc64v1::link),
#[cfg(feature = "s390x")]
("s390x", mold_target_s390x::link),
#[cfg(feature = "sparc64")]
("sparc64", mold_target_sparc64::link),
#[cfg(feature = "m68k")]
("m68k", mold_target_m68k::link),
#[cfg(feature = "sh4")]
("sh4", mold_target_sh4::link),
#[cfg(feature = "sh4be")]
("sh4be", mold_target_sh4be::link),
#[cfg(feature = "loongarch64")]
("loongarch64", mold_target_loongarch64::link),
#[cfg(feature = "loongarch32")]
("loongarch32", mold_target_loongarch32::link),
];
fn link_for_target(target: &str, cmdline: mold::driver::Cmdline) -> mold::driver::LinkResult {
for &(name, link) in TARGETS {
if name == target {
return link(cmdline);
}
}
eprintln!(
"mold: unsupported target: {target}; rebuild mold with the appropriate target support"
);
std::process::exit(1);
}
fn main() {
let Some(&(initial_target, _)) = TARGETS.first() else {
eprintln!("mold: no targets enabled; rebuild mold with the appropriate target support");
std::process::exit(1);
};
let args = std::env::args_os().collect();
let status = mold::driver::main(args, initial_target, link_for_target);
std::process::exit(status);
}
-177
View File
@@ -1,177 +0,0 @@
// This file contains functions to read an archive file (.a file).
// An archive file is just a bundle of object files. It's similar to
// tar or zip, but the contents are not compressed.
//
// An archive file is either "regular" or "thin". A regular archive
// contains object files directly, while a thin archive contains only
// pathnames. In the latter case, actual file contents have to be read
// from given pathnames. A regular archive is sometimes called "fat"
// archive as opposed to "thin".
//
// If an archive file is given to the linker, the linker pulls out
// object files that are needed to resolve undefined symbols. So,
// bunding object files as an archive and giving that archive to the
// linker has a different meaning than directly giving the same set of
// object files to the linker. The former links only needed object
// files, while the latter links all the given object files.
//
// Therefore, if you link libc.a for example, not all the libc
// functions are linked to your binary. Instead, only object files
// that provides functions and variables used in your program get
// linked. To make this efficient, static library functions are
// usually separated to each object file in an archive file. You can
// see the contents of libc.a by running `ar t
// /usr/lib/x86_64-linux-gnu/libc.a`.
#pragma once
#include "common.h"
#include "filetype.h"
namespace mold {
struct ArHdr {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
bool starts_with(std::string_view s) const {
return std::string_view(ar_name, s.size()) == s;
}
bool is_strtab() const {
return starts_with("// ");
}
bool is_symtab() const {
return starts_with("/ ") || starts_with("/SYM64/ ");
}
std::string read_name(std::string_view strtab, u8 *&ptr) const {
// BSD-style long filename
if (starts_with("#1/")) {
int namelen = atoi(ar_name + 3);
std::string name{(char *)ptr, (size_t)namelen};
ptr += namelen;
if (size_t pos = name.find('\0'))
name = name.substr(0, pos);
return name;
}
// SysV-style long filename
if (starts_with("/")) {
const char *start = strtab.data() + atoi(ar_name + 1);
return {start, (const char *)strstr(start, "/\n")};
}
// Short fileanme
if (const char *end = (char *)memchr(ar_name, '/', sizeof(ar_name)))
return {ar_name, end};
return {ar_name, sizeof(ar_name)};
}
};
template <typename Context, typename MappedFile>
std::vector<MappedFile *>
read_thin_archive_members(Context &ctx, MappedFile *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile *> vec;
std::string_view strtab;
while (data < begin + mf->size) {
// Each header is aligned to a 2 byte boundary.
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
// Read a string table.
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
data = body + size;
continue;
}
// Skip a symbol table.
if (hdr.is_symtab()) {
data = body + size;
continue;
}
if (!hdr.starts_with("#1/") && !hdr.starts_with("/"))
Fatal(ctx) << mf->name << ": filename is not stored as a long filename";
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile::must_open(ctx, path));
vec.back()->thin_parent = mf;
data = body;
}
return vec;
}
template <typename Context, typename MappedFile>
std::vector<MappedFile *> read_fat_archive_members(Context &ctx, MappedFile *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile *> vec;
std::string_view strtab;
while (begin + mf->size - data >= 2) {
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
data = body + size;
// Read if string table
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
continue;
}
// Skip if symbol table
if (hdr.is_symtab())
continue;
// Read the name field
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
vec.push_back(mf->slice(ctx, name, body - begin, data - body));
}
return vec;
}
template <typename Context, typename MappedFile>
std::vector<MappedFile *> read_archive_members(Context &ctx, MappedFile *mf) {
switch (get_file_type(ctx, mf)) {
case FileType::AR:
return read_fat_archive_members(ctx, mf);
case FileType::THIN_AR:
return read_thin_archive_members(ctx, mf);
default:
unreachable();
}
}
} // namespace mold
-101
View File
@@ -1,101 +0,0 @@
#pragma once
#include "common.h"
namespace mold {
template <typename Context>
std::vector<std::string_view>
read_response_file(Context &ctx, std::string_view path, i64 depth) {
if (depth > 10)
Fatal(ctx) << path << ": response file nesting too deep";
std::vector<std::string_view> vec;
MappedFile<Context> *mf = MappedFile<Context>::must_open(ctx, std::string(path));
std::string_view data((char *)mf->data, mf->size);
while (!data.empty()) {
if (isspace(data[0])) {
data = data.substr(1);
continue;
}
auto read_quoted = [&]() {
char quote = data[0];
data = data.substr(1);
std::string buf;
while (!data.empty() && data[0] != quote) {
if (data[0] == '\\' && data.size() >= 1) {
buf.append(1, data[1]);
data = data.substr(2);
} else {
buf.append(1, data[0]);
data = data.substr(1);
}
}
if (data.empty())
Fatal(ctx) << path << ": premature end of input";
data = data.substr(1);
return save_string(ctx, buf);
};
auto read_unquoted = [&] {
std::string buf;
while (!data.empty()) {
if (data[0] == '\\' && data.size() >= 1) {
buf.append(1, data[1]);
data = data.substr(2);
continue;
}
if (!isspace(data[0])) {
buf.append(1, data[0]);
data = data.substr(1);
continue;
}
break;
}
return save_string(ctx, buf);
};
std::string_view tok;
if (data[0] == '\'' || data[0] == '\"')
tok = read_quoted();
else
tok = read_unquoted();
if (tok.starts_with('@'))
append(vec, read_response_file(ctx, tok.substr(1), depth + 1));
else
vec.push_back(tok);
}
return vec;
}
// Replace "@path/to/some/text/file" with its file contents.
template <typename Context>
std::vector<std::string_view> expand_response_files(Context &ctx, char **argv) {
std::vector<std::string_view> vec;
for (i64 i = 0; argv[i]; i++) {
if (argv[i][0] == '@')
append(vec, read_response_file(ctx, argv[i] + 1, 1));
else
vec.push_back(argv[i]);
}
return vec;
}
static inline std::string_view string_trim(std::string_view str) {
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos)
return "";
str = str.substr(pos);
pos = str.find_last_not_of(" \t");
if (pos == str.npos)
return str;
return str.substr(0, pos + 1);
}
} // namespace mold
-1114
View File
File diff suppressed because it is too large. Load diff
-185
View File
@@ -1,185 +0,0 @@
// This file implements a multi-threaded zlib and zstd compression
// routine.
//
// zlib-compressed data can be merged just by concatenation as long as
// each piece of data is flushed with Z_SYNC_FLUSH. In this file, we
// split input data into multiple shards, compress them individually
// and concatenate them. We then append a header, a trailer and a
// checksum so that the concatenated data is valid zlib-format data.
//
// zstd-compressed data can be merged in the same way.
//
// Using threads to compress data has a downside. Since the dictionary
// is reset on boundaries of shards, compression ratio is sacrificed
// a little bit. However, if a shard size is large enough, that loss
// is negligible in practice.
#include "common.h"
#include <tbb/parallel_for_each.h>
#include <zlib.h>
#include <zstd.h>
#define CHECK(fn) \
do { \
[[maybe_unused]] int r = (fn); \
assert(r == Z_OK); \
} while (0)
namespace mold {
static constexpr i64 SHARD_SIZE = 1024 * 1024;
static std::vector<std::string_view> split(std::string_view input) {
std::vector<std::string_view> shards;
while (input.size() >= SHARD_SIZE) {
shards.push_back(input.substr(0, SHARD_SIZE));
input = input.substr(SHARD_SIZE);
}
if (!input.empty())
shards.push_back(input);
return shards;
}
static std::vector<u8> zlib_compress(std::string_view input) {
// Initialize zlib stream. Since debug info is generally compressed
// pretty well with lower compression levels, we chose compression
// level 1.
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
CHECK(deflateInit2(&strm, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY));
// Set an input buffer
strm.avail_in = input.size();
strm.next_in = (u8 *)input.data();
// Set an output buffer. deflateBound() returns an upper bound
// on the compression size. +16 for Z_SYNC_FLUSH.
std::vector<u8> buf(deflateBound(&strm, strm.avail_in) + 16);
// Compress data. It writes all compressed bytes except the last
// partial byte, so up to 7 bits can be held to be written to the
// buffer.
strm.avail_out = buf.size();
strm.next_out = buf.data();
CHECK(deflate(&strm, Z_BLOCK));
// This is a workaround for libbacktrace before 2022-04-06.
//
// Zlib is a bit stream, and what Z_SYNC_FLUSH does is to write a
// three bit value indicating the start of an uncompressed data
// block followed by four byte data 00 00 ff ff which indicates that
// the length of the block is zero. libbacktrace uses its own zlib
// inflate routine, and it had a bug that if that particular three
// bit value happens to end at a byte boundary, it accidentally
// skipped the next byte.
//
// In order to avoid triggering that bug, we should avoid calling
// deflate() with Z_SYNC_FLUSH if the current bit position is 5.
// If it's 5, we insert an empty block consisting of 10 bits so
// that the bit position is 7 in the next byte.
//
// https://github.com/ianlancetaylor/libbacktrace/pull/87
int nbits;
deflatePending(&strm, Z_NULL, &nbits);
if (nbits == 5)
CHECK(deflatePrime(&strm, 10, 2));
CHECK(deflate(&strm, Z_SYNC_FLUSH));
assert(strm.avail_out > 0);
buf.resize(buf.size() - strm.avail_out);
buf.shrink_to_fit();
deflateEnd(&strm);
return buf;
}
ZlibCompressor::ZlibCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
std::vector<u64> adlers(inputs.size());
shards.resize(inputs.size());
// Compress each shard
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
adlers[i] = adler32(1, (u8 *)inputs[i].data(), inputs[i].size());
shards[i] = zlib_compress(inputs[i]);
});
// Combine checksums
checksum = adlers[0];
for (i64 i = 1; i < inputs.size(); i++)
checksum = adler32_combine(checksum, adlers[i], inputs[i].size());
// Comput the total size
compressed_size = 8; // the header and the trailer
for (std::vector<u8> &shard : shards)
compressed_size += shard.size();
}
void ZlibCompressor::write_to(u8 *buf) {
// Write a zlib-format header
buf[0] = 0x78;
buf[1] = 0x9c;
// Copy compressed data
std::vector<i64> offsets(shards.size());
offsets[0] = 2; // +2 for header
for (i64 i = 1; i < shards.size(); i++)
offsets[i] = offsets[i - 1] + shards[i - 1].size();
tbb::parallel_for((i64)0, (i64)shards.size(), [&](i64 i) {
memcpy(&buf[offsets[i]], shards[i].data(), shards[i].size());
});
// Write a trailer
u8 *end = buf + compressed_size;
end[-6] = 3;
end[-5] = 0;
// Write a checksum
*(ub32 *)(end - 4) = checksum;
}
static std::vector<u8> zstd_compress(std::string_view input) {
std::vector<u8> buf(ZSTD_COMPRESSBOUND(input.size()));
constexpr int level = 3; // compression level; must be between 1 to 22
size_t sz = ZSTD_compress(buf.data(), buf.size(), input.data(), input.size(),
level);
assert(!ZSTD_isError(sz));
buf.resize(sz);
buf.shrink_to_fit();
return buf;
}
ZstdCompressor::ZstdCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
shards.resize(inputs.size());
// Compress each shard
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
shards[i] = zstd_compress(inputs[i]);
});
compressed_size = 0;
for (std::vector<u8> &shard : shards)
compressed_size += shard.size();
}
void ZstdCompressor::write_to(u8 *buf) {
// Copy compressed data
std::vector<i64> offsets(shards.size());
for (i64 i = 1; i < shards.size(); i++)
offsets[i] = offsets[i - 1] + shards[i - 1].size();
tbb::parallel_for((i64)0, (i64)shards.size(), [&](i64 i) {
memcpy(&buf[offsets[i]], shards[i].data(), shards[i].size());
});
}
} // namespace mold
-4
View File
@@ -1,4 +0,0 @@
#define MOLD_VERSION "@mold_VERSION@"
#define MOLD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@"
#cmakedefine HAVE_MADVISE
-42
View File
@@ -1,42 +0,0 @@
#include "common.h"
#include <cstdlib>
#ifndef _WIN32
#include <cxxabi.h>
#endif
#include "../third-party/rust-demangle/rust-demangle.h"
namespace mold {
std::optional<std::string_view> demangle_cpp(std::string_view name) {
static thread_local char *buf;
static thread_local size_t buflen;
// TODO(cwasser): Actually demangle Symbols on Windows using e.g.
// `UnDecorateSymbolName` from Dbghelp, maybe even Itanium symbols?
#ifndef _WIN32
if (name.starts_with("_Z")) {
int status;
char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
if (status == 0) {
buf = p;
return p;
}
}
#endif
return {};
}
std::optional<std::string_view> demangle_rust(std::string_view name) {
static thread_local char *buf;
free(buf);
buf = rust_demangle(std::string(name).c_str(), 0);
if (buf)
return buf;
return {};
}
} // namespace mold
-29
View File
@@ -1,29 +0,0 @@
#include "common.h"
#include <filesystem>
#include <sys/stat.h>
namespace mold {
std::string get_realpath(std::string_view path) {
std::error_code ec;
std::filesystem::path link = std::filesystem::read_symlink(path, ec);
if (ec)
return std::string(path);
return (filepath(path) / ".." / link).lexically_normal().string();
}
// Removes redundant '/..' or '/.' from a given path.
// The transformation is done purely by lexical processing.
// This function does not access file system.
std::string path_clean(std::string_view path) {
return filepath(path).lexically_normal().string();
}
std::filesystem::path to_abs_path(std::filesystem::path path) {
if (path.is_absolute())
return path.lexically_normal();
return (std::filesystem::current_path() / path).lexically_normal();
}
} // namespace mold
-193
View File
@@ -1,193 +0,0 @@
#pragma once
#include "common.h"
#include "../elf/elf.h"
namespace mold {
enum class FileType {
UNKNOWN,
EMPTY,
ELF_OBJ,
ELF_DSO,
MACH_OBJ,
MACH_EXE,
MACH_DYLIB,
MACH_BUNDLE,
MACH_UNIVERSAL,
AR,
THIN_AR,
TAPI,
TEXT,
GCC_LTO_OBJ,
LLVM_BITCODE,
};
template <typename MappedFile>
bool is_text_file(MappedFile *mf) {
u8 *data = mf->data;
return mf->size >= 4 && isprint(data[0]) && isprint(data[1]) &&
isprint(data[2]) && isprint(data[3]);
}
template <typename E, typename Context, typename MappedFile>
inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
using namespace mold::elf;
const char *data = mf->get_contents().data();
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(data + ehdr.e_shoff);
std::span<ElfShdr<E>> shdrs{(ElfShdr<E> *)(data + ehdr.e_shoff), ehdr.e_shnum};
// e_shstrndx is a 16-bit field. If .shstrtab's section index is
// too large, the actual number is stored to sh_link field.
i64 shstrtab_idx = (ehdr.e_shstrndx == SHN_XINDEX)
? sh_begin->sh_link : ehdr.e_shstrndx;
for (ElfShdr<E> &sec : shdrs) {
// GCC FAT LTO objects contain both regular ELF sections and GCC-
// specific LTO sections, so that they can be linked as LTO objects if
// the LTO linker plugin is available and falls back as regular
// objects otherwise. GCC FAT LTO object can be identified by the
// presence of `.gcc.lto_.symtab` section.
if (!ctx.arg.plugin.empty()) {
std::string_view name = data + shdrs[shstrtab_idx].sh_offset + sec.sh_name;
if (name.starts_with(".gnu.lto_.symtab."))
return true;
}
if (sec.sh_type != SHT_SYMTAB)
continue;
// GCC non-FAT LTO object contains only sections symbols followed by
// a common symbol whose name is `__gnu_lto_slim` (or `__gnu_lto_v1`
// for older GCC releases).
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
(size_t)sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
};
i64 i = 1;
while (i < elf_syms.size() && skip(elf_syms[i].st_type))
i++;
if (i < elf_syms.size() && elf_syms[i].st_shndx == SHN_COMMON) {
std::string_view name =
data + shdrs[sec.sh_link].sh_offset + elf_syms[i].st_name;
if (name.starts_with("__gnu_lto_"))
return true;
}
break;
}
return false;
}
template <typename Context, typename MappedFile>
FileType get_file_type(Context &ctx, MappedFile *mf) {
using namespace elf;
std::string_view data = mf->get_contents();
if (data.empty())
return FileType::EMPTY;
if (data.starts_with("\177ELF")) {
u8 byte_order = ((ElfEhdr<I386> *)data.data())->e_ident[EI_DATA];
if (byte_order == ELFDATA2LSB) {
auto &ehdr = *(ElfEhdr<I386> *)data.data();
if (ehdr.e_type == ET_REL) {
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
if (is_gcc_lto_obj<I386>(ctx, mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<X86_64>(ctx, mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
if (ehdr.e_type == ET_DYN)
return FileType::ELF_DSO;
} else {
auto &ehdr = *(ElfEhdr<M68K> *)data.data();
if (ehdr.e_type == ET_REL) {
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
if (is_gcc_lto_obj<M68K>(ctx, mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<SPARC64>(ctx, mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
if (ehdr.e_type == ET_DYN)
return FileType::ELF_DSO;
}
return FileType::UNKNOWN;
}
if (data.starts_with("\xcf\xfa\xed\xfe")) {
switch (*(ul32 *)(data.data() + 12)) {
case 1: // MH_OBJECT
return FileType::MACH_OBJ;
case 2: // MH_EXECUTE
return FileType::MACH_EXE;
case 6: // MH_DYLIB
return FileType::MACH_DYLIB;
case 8: // MH_BUNDLE
return FileType::MACH_BUNDLE;
}
return FileType::UNKNOWN;
}
if (data.starts_with("!<arch>\n"))
return FileType::AR;
if (data.starts_with("!<thin>\n"))
return FileType::THIN_AR;
if (data.starts_with("--- !tapi-tbd"))
return FileType::TAPI;
if (data.starts_with("\xca\xfe\xba\xbe"))
return FileType::MACH_UNIVERSAL;
if (is_text_file(mf))
return FileType::TEXT;
if (data.starts_with("\xde\xc0\x17\x0b"))
return FileType::LLVM_BITCODE;
if (data.starts_with("BC\xc0\xde"))
return FileType::LLVM_BITCODE;
return FileType::UNKNOWN;
}
inline std::string filetype_to_string(FileType type) {
switch (type) {
case FileType::UNKNOWN: return "UNKNOWN";
case FileType::EMPTY: return "EMPTY";
case FileType::ELF_OBJ: return "ELF_OBJ";
case FileType::ELF_DSO: return "ELF_DSO";
case FileType::MACH_EXE: return "MACH_EXE";
case FileType::MACH_OBJ: return "MACH_OBJ";
case FileType::MACH_DYLIB: return "MACH_DYLIB";
case FileType::MACH_BUNDLE: return "MACH_BUNDLE";
case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
case FileType::AR: return "AR";
case FileType::THIN_AR: return "THIN_AR";
case FileType::TAPI: return "TAPI";
case FileType::TEXT: return "TEXT";
case FileType::GCC_LTO_OBJ: return "GCC_LTO_OBJ";
case FileType::LLVM_BITCODE: return "LLVM_BITCODE";
}
return "UNKNOWN";
}
inline std::ostream &operator<<(std::ostream &out, FileType type) {
out << filetype_to_string(type);
return out;
}
} // namespace mold
-157
View File
@@ -1,157 +0,0 @@
#include "common.h"
#include <cstring>
namespace mold {
std::optional<Glob> Glob::compile(std::string_view pat) {
std::vector<Element> vec;
while (!pat.empty()) {
u8 c = pat[0];
pat = pat.substr(1);
switch (c) {
case '[': {
// Here are a few bracket pattern examples:
//
// [abc]: a, b or c
// [$\]!]: $, ] or !
// [a-czg-i]: a, b, c, z, g, h, or i
// [^a-z]: Any character except lowercase letters
vec.push_back({BRACKET});
std::bitset<256> &bitset = vec.back().bitset;
bool negate = false;
if (!pat.empty() && pat[0] == '^') {
negate = true;
pat = pat.substr(1);
}
bool closed = false;
while (!pat.empty()) {
if (pat[0] == ']') {
pat = pat.substr(1);
closed = true;
break;
}
if (pat[0] == '\\') {
pat = pat.substr(1);
if (pat.empty())
return {};
}
if (pat.size() >= 3 && pat[1] == '-') {
u8 start = pat[0];
u8 end = pat[2];
pat = pat.substr(3);
if (end == '\\') {
if (pat.empty())
return {};
end = pat[0];
pat = pat.substr(1);
}
if (end < start)
return {};
for (i64 i = start; i <= end; i++)
bitset[i] = true;
} else {
bitset[(u8)pat[0]] = true;
pat = pat.substr(1);
}
}
if (!closed)
return {};
if (negate)
bitset.flip();
break;
}
case '?':
vec.push_back({QUESTION});
break;
case '*':
vec.push_back({STAR});
break;
case '\\':
if (pat.empty())
return {};
if (vec.empty() || vec.back().kind != STRING)
vec.push_back({STRING});
vec.back().str += pat[0];
pat = pat.substr(1);
break;
default:
if (vec.empty() || vec.back().kind != STRING)
vec.push_back({STRING});
vec.back().str += c;
break;
}
}
return {Glob{std::move(vec)}};
}
bool Glob::match(std::string_view str) {
return do_match(str, elements);
}
bool Glob::do_match(std::string_view str, std::span<Element> elements) {
while (!elements.empty()) {
Element &e = elements[0];
elements = elements.subspan(1);
switch (e.kind) {
case STRING:
if (!str.starts_with(e.str))
return false;
str = str.substr(e.str.size());
break;
case STAR:
if (elements.empty())
return true;
// Patterns like "*foo*bar*" should be much more common than more
// complex ones like "*foo*[abc]*" or "*foo**?bar*", so we optimize
// the former case here.
if (elements[0].kind == STRING) {
for (;;) {
size_t pos = str.find(elements[0].str);
if (pos == str.npos)
break;
if (do_match(str.substr(pos + elements[0].str.size()),
elements.subspan(1)))
return true;
str = str.substr(pos + 1);
}
return false;
}
// Other cases are handled here.
for (i64 j = 0; j < str.size(); j++)
if (do_match(str.substr(j), elements))
return true;
return false;
case QUESTION:
if (str.empty())
return false;
str = str.substr(1);
break;
case BRACKET:
if (str.empty() || !e.bitset[str[0]])
return false;
str = str.substr(1);
break;
}
}
return str.empty();
}
} // namespace mold
-20
View File
@@ -1,20 +0,0 @@
// This file implements HyperLogLog algorithm, which estimates
// the number of unique items in a given multiset.
//
// For more info, read
// https://engineering.fb.com/2018/12/13/data-infrastructure/hyperloglog
#include "common.h"
#include <cmath>
namespace mold {
i64 HyperLogLog::get_cardinality() const {
double z = 0;
for (i64 val : buckets)
z += std::ldexp(1.0, -val);
return ALPHA * NBUCKETS * NBUCKETS / z;
}
} // namespace mold
-221
View File
@@ -1,221 +0,0 @@
// This file defines integral types for file input/output. We need to use
// these types instead of the plain integers (such as uint32_t or int32_t)
// when reading from/writing to an mmap'ed file area for the following
// reasons:
//
// 1. mold is always a cross linker and should not depend on what host it
// is running on. Users should be able to run mold on a big-endian
// SPARC machine to create a little-endian RV64 binary, for example.
//
// 2. Even though data members in all ELF data strucutres are naturally
// aligned, they are not guaranteed to be aligned on memory. Because
// archive file (.a file) aligns each member only to a 2 byte boundary,
// anything larger than 2 bytes may be unaligned in an mmap'ed memory.
// Unaligned access is an undefined behavior in C/C++, so we shouldn't
// cast an arbitrary pointer to a uint32_t, for example, to read a
// 32-bits value.
//
// The data types defined in this file don't depend on host byte order and
// don't do unaligned access.
#pragma once
#include <bit>
#include <cstdint>
#include <cstring>
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define __LITTLE_ENDIAN__ 1
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __BIG_ENDIAN__ 1
# else
# error "unknown host byte order"
# endif
#endif
namespace mold {
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
template <typename T>
static inline T bswap(T val) {
switch (sizeof(T)) {
case 2: return __builtin_bswap16(val);
case 4: return __builtin_bswap32(val);
case 8: return __builtin_bswap64(val);
default: __builtin_unreachable();
}
}
template <typename T, int SIZE = sizeof(T)>
class LittleEndian {
public:
LittleEndian() = default;
LittleEndian(T x) { *this = x; }
operator T() const {
if constexpr (sizeof(T) == SIZE) {
T x;
memcpy(&x, val, sizeof(T));
if constexpr (std::endian::native == std::endian::big)
x = bswap(x);
return x;
} else {
static_assert(SIZE == 3);
return (val[2] << 16) | (val[1] << 8) | val[0];
}
}
LittleEndian &operator=(T x) {
if constexpr (sizeof(T) == SIZE) {
if constexpr (std::endian::native == std::endian::big)
x = bswap(x);
memcpy(val, &x, sizeof(T));
} else {
static_assert(SIZE == 3);
val[2] = x >> 16;
val[1] = x >> 8;
val[0] = x;
}
return *this;
}
LittleEndian &operator++() {
return *this = *this + 1;
}
LittleEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
LittleEndian &operator--() {
return *this = *this - 1;
}
LittleEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
LittleEndian &operator+=(T x) {
return *this = *this + x;
}
LittleEndian &operator-=(T x) {
return *this = *this - x;
}
LittleEndian &operator&=(T x) {
return *this = *this & x;
}
LittleEndian &operator|=(T x) {
return *this = *this | x;
}
private:
u8 val[SIZE];
};
using il16 = LittleEndian<i16>;
using il32 = LittleEndian<i32>;
using il64 = LittleEndian<i64>;
using ul16 = LittleEndian<u16>;
using ul24 = LittleEndian<u32, 3>;
using ul32 = LittleEndian<u32>;
using ul64 = LittleEndian<u64>;
template <typename T, int SIZE = sizeof(T)>
class BigEndian {
public:
BigEndian() = default;
BigEndian(T x) { *this = x; }
operator T() const {
if constexpr (sizeof(T) == SIZE) {
T x;
memcpy(&x, val, sizeof(T));
if constexpr (std::endian::native == std::endian::little)
x = bswap(x);
return x;
} else {
static_assert(SIZE == 3);
return (val[0] << 16) | (val[1] << 8) | val[2];
}
}
BigEndian &operator=(T x) {
if constexpr (sizeof(T) == SIZE) {
if constexpr (std::endian::native == std::endian::little)
x = bswap(x);
memcpy(val, &x, sizeof(T));
} else {
static_assert(SIZE == 3);
val[0] = x >> 16;
val[1] = x >> 8;
val[2] = x;
}
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator-=(T x) {
return *this = *this - x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
u8 val[SIZE];
};
using ib16 = BigEndian<i16>;
using ib32 = BigEndian<i32>;
using ib64 = BigEndian<i64>;
using ub16 = BigEndian<u16>;
using ub24 = BigEndian<u32, 3>;
using ub32 = BigEndian<u32>;
using ub64 = BigEndian<u64>;
} // namespace mold
-192
View File
@@ -1,192 +0,0 @@
#include "common.h"
#include "config.h"
#include <cstring>
#include <filesystem>
#include <signal.h>
#include <tbb/global_control.h>
#include <tbb/version.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifdef __FreeBSD__
# include <sys/sysctl.h>
# include <unistd.h>
#endif
#ifdef _WIN32
# define unlink _unlink
# define write _write
#endif
namespace mold {
std::string mold_version_string = MOLD_VERSION;
namespace elf {
int main(int argc, char **argv);
}
namespace macho {
int main(int argc, char **argv);
}
static std::string get_mold_version() {
if (mold_git_hash.empty())
return "mold "s + MOLD_VERSION + " (compatible with GNU ld)";
return "mold "s + MOLD_VERSION + " (" + mold_git_hash +
"; compatible with GNU ld)";
}
void cleanup() {
if (output_tmpfile)
unlink(output_tmpfile);
}
std::string errno_string() {
// strerror is not thread-safe, so guard it with a lock.
static std::mutex mu;
std::scoped_lock lock(mu);
return strerror(errno);
}
// Returns the path of the mold executable itself
std::string get_self_path() {
#if __APPLE__
char path[8192];
u32 size = sizeof(path);
if (_NSGetExecutablePath(path, &size)) {
std::cerr << "_NSGetExecutablePath failed\n";
exit(1);
}
return path;
#elif __FreeBSD__
// /proc may not be mounted on FreeBSD. The proper way to get the
// current executable's path is to use sysctl(2).
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
size_t size;
sysctl(mib, 4, NULL, &size, NULL, 0);
std::string path;
path.resize(size);
sysctl(mib, 4, path.data(), &size, NULL, 0);
return path;
#else
return std::filesystem::read_symlink("/proc/self/exe").string();
#endif
}
// mold mmap's an output file, and the mmap succeeds even if there's
// no enough space left on the filesystem. The actual disk blocks are
// not allocated on the mmap call but when the program writes to it
// for the first time.
//
// If a disk becomes full as a result of a write to an mmap'ed memory
// region, the failure of the write is reported as a SIGBUS or structured
// exeption with code EXCEPTION_IN_PAGE_ERROR on Windows. This
// signal handler catches that signal and prints out a user-friendly
// error message. Without this, it is very hard to realize that the
// disk might be full.
#ifdef _WIN32
static LONG WINAPI vectored_handler(_EXCEPTION_POINTERS *exception_info) {
static std::mutex mu;
std::scoped_lock lock{mu};
PEXCEPTION_RECORD exception_record = exception_info->ExceptionRecord;
ULONG_PTR *exception_information = exception_record->ExceptionInformation;
if (exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR &&
(ULONG_PTR)output_buffer_start <= exception_information[1] &&
exception_information[1] < (ULONG_PTR)output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(_fileno(stderr), msg, sizeof(msg) - 1);
}
cleanup();
_exit(1);
}
void install_signal_handler() {
AddVectoredExceptionHandler(0, vectored_handler);
}
#else
static std::string sigabrt_msg;
static void sighandler(int signo, siginfo_t *info, void *ucontext) {
static std::mutex mu;
std::scoped_lock lock{mu};
// Handle disk full error
switch (signo) {
case SIGSEGV:
case SIGBUS:
if (output_buffer_start <= info->si_addr &&
info->si_addr < output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
}
break;
case SIGABRT: {
(void)!write(STDERR_FILENO, &sigabrt_msg[0], sigabrt_msg.size());
break;
}
}
// Re-throw the signal
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGABRT, SIG_DFL);
raise(signo);
}
void install_signal_handler() {
struct sigaction action;
action.sa_sigaction = sighandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, NULL);
sigaction(SIGBUS, &action, NULL);
// OneTBB 2021.9.0 has the interface version 12090.
if (TBB_runtime_interface_version() < 12090) {
sigabrt_msg = "mold: aborted\n"
"mold: mold with libtbb version 2021.9.0 or older is known to be unstable "
"under heavy load. Your libtbb version is " +
std::string(TBB_runtime_version()) +
". Please upgrade your libtbb library and try again.\n";
sigaction(SIGABRT, &action, NULL);
}
}
#endif
i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
} // namespace mold
int main(int argc, char **argv) {
mold::mold_version = mold::get_mold_version();
return mold::elf::main(argc, argv);
}
-166
View File
@@ -1,166 +0,0 @@
// This file implements the Aho-Corasick algorithm to match multiple
// glob patterns to symbol strings as quickly as possible.
//
// Here are some examples of glob patterns:
//
// qt_private_api_tag*
// *16QAccessibleCache*
// *32QAbstractFileIconProviderPrivate*
// *17QPixmapIconEngine*
//
// `*` is a wildcard that matches any substring. We sometimes have
// hundreds of glob patterns and have to match them against millions
// of symbol strings.
//
// Aho-Corasick cannot handle complex patterns such as `*foo*bar*`.
// We handle such patterns with the Glob class. Glob is relatively
// slow, but complex patterns are rare in practice, so it should be
// OK.
#include "common.h"
#include <queue>
#include <regex>
namespace mold {
std::optional<i64> MultiGlob::find(std::string_view str) {
std::call_once(once, [&] { compile(); });
i64 val = -1;
if (root) {
// Match against simple glob patterns
TrieNode *node = root.get();
auto walk = [&](u8 c) {
for (;;) {
if (node->children[c]) {
node = node->children[c].get();
val = std::max(val, node->value);
return;
}
if (!node->suffix_link)
return;
node = node->suffix_link;
}
};
walk('\0');
for (u8 c : str)
walk(c);
walk('\0');
}
// Match against complex glob patterns
for (std::pair<Glob, i64> &glob : globs)
if (glob.first.match(str))
val = std::max(val, glob.second);
if (val == -1)
return {};
return val;
}
static bool is_simple_pattern(std::string_view pat) {
static std::regex re(R"(\*?[^*[?]+\*?)", std::regex_constants::optimize);
return std::regex_match(pat.begin(), pat.end(), re);
}
static std::string handle_stars(std::string_view pat) {
std::string str(pat);
// Convert "foo" -> "\0foo\0", "*foo" -> "foo\0", "foo*" -> "\0foo"
// and "*foo*" -> "foo". Aho-Corasick can do only substring matching,
// so we use \0 as beginning/end-of-string markers.
if (str.starts_with('*') && str.ends_with('*'))
return str.substr(1, str.size() - 2);
if (str.starts_with('*'))
return str.substr(1) + "\0"s;
if (str.ends_with('*'))
return "\0"s + str.substr(0, str.size() - 1);
return "\0"s + str + "\0"s;
}
bool MultiGlob::add(std::string_view pat, i64 val) {
assert(!is_compiled);
assert(!pat.empty());
strings.push_back(std::string(pat));
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<Glob> glob = Glob::compile(pat)) {
globs.push_back({std::move(*glob), val});
return true;
}
return false;
}
// Simple glob pattern
if (!root)
root.reset(new TrieNode);
TrieNode *node = root.get();
for (u8 c : handle_stars(pat)) {
if (!node->children[c])
node->children[c].reset(new TrieNode);
node = node->children[c].get();
}
node->value = std::max(node->value, val);
return true;
}
void MultiGlob::compile() {
is_compiled = true;
if (root) {
fix_suffix_links(*root);
fix_values();
}
}
void MultiGlob::fix_suffix_links(TrieNode &node) {
for (i64 i = 0; i < 256; i++) {
if (!node.children[i])
continue;
TrieNode &child = *node.children[i];
TrieNode *cur = node.suffix_link;
for (;;) {
if (!cur) {
child.suffix_link = root.get();
break;
}
if (cur->children[i]) {
child.suffix_link = cur->children[i].get();
break;
}
cur = cur->suffix_link;
}
fix_suffix_links(child);
}
}
void MultiGlob::fix_values() {
std::queue<TrieNode *> queue;
queue.push(root.get());
do {
TrieNode *node = queue.front();
queue.pop();
for (std::unique_ptr<TrieNode> &child : node->children) {
if (!child)
continue;
child->value = std::max(child->value, child->suffix_link->value);
queue.push(child.get());
}
} while (!queue.empty());
}
} // namespace mold
-149
View File
@@ -1,149 +0,0 @@
#include "common.h"
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold {
inline u32 get_umask() {
u32 orig_umask = umask(0);
umask(orig_umask);
return orig_umask;
}
template <typename Context>
static std::pair<i64, char *>
open_or_create_file(Context &ctx, std::string path, i64 filesize, i64 perm) {
std::string tmpl = filepath(path).parent_path() / ".mold-XXXXXX";
char *path2 = (char *)save_string(ctx, tmpl).data();
i64 fd = mkstemp(path2);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh
// file and writing to it.
if (ctx.overwrite_output_file && rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
return {fd, path2};
unlink(path2);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
}
if (fchmod(fd, (perm & ~get_umask())) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
#ifdef __linux__
if (fallocate(fd, 0, 0, filesize) == 0)
return {fd, path2};
#endif
if (ftruncate(fd, filesize) == -1)
Fatal(ctx) << "ftruncate failed: " << errno_string();
return {fd, path2};
}
template <typename Context>
class MemoryMappedOutputFile : public OutputFile<Context> {
public:
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(path, filesize, true) {
std::tie(this->fd, output_tmpfile) =
open_or_create_file(ctx, path, filesize, perm);
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, this->fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
mold::output_buffer_start = this->buf;
mold::output_buffer_end = this->buf + filesize;
}
~MemoryMappedOutputFile() {
if (fd2 != -1)
::close(fd2);
}
void close(Context &ctx) override {
Timer t(ctx, "close_file");
if (!this->is_unmapped)
munmap(this->buf, this->filesize);
if (this->buf2.empty()) {
::close(this->fd);
} else {
FILE *out = fdopen(this->fd, "w");
fseek(out, 0, SEEK_END);
fwrite(&this->buf2[0], this->buf2.size(), 1, out);
fclose(out);
}
// If an output file already exists, open a file and then remove it.
// This is the fastest way to unlink a file, as it does not make the
// system to immediately release disk blocks occupied by the file.
fd2 = ::open(this->path.c_str(), O_RDONLY);
if (fd2 != -1)
unlink(this->path.c_str());
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
private:
int fd2 = -1;
};
template <typename Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
OutputFile<Context> *file;
if (is_special)
file = new MallocOutputFile(ctx, path, filesize, perm);
else
file = new MemoryMappedOutputFile(ctx, path, filesize, perm);
#ifdef MADV_HUGEPAGE
// Enable transparent huge page for an output memory-mapped file.
// On Linux, it has an effect only on tmpfs mounted with `huge=advise`,
// but it can make the linker ~10% faster. You can try it by creating
// a tmpfs with the following commands
//
// $ mkdir tmp
// $ sudo mount -t tmpfs -o size=2G,huge=advise none tmp
//
// and then specifying a path under the directory as an output file.
madvise(file->buf, filesize, MADV_HUGEPAGE);
#endif
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return std::unique_ptr<OutputFile>(file);
}
} // namespace mold
-24
View File
@@ -1,24 +0,0 @@
#include "common.h"
#include <fcntl.h>
#include <filesystem>
#include <windows.h>
namespace mold {
template <typename Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
OutputFile<Context> *file = new MallocOutputFile(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return std::unique_ptr<OutputFile<Context>>(file);
}
} // namespace mold
-5
View File
@@ -1,5 +0,0 @@
#if _WIN32
# include "output-file-win32.h"
#else
# include "output-file-unix.h"
#endif
-125
View File
@@ -1,125 +0,0 @@
#include "common.h"
#include <functional>
#include <iomanip>
#include <ios>
#ifndef _WIN32
#include <sys/resource.h>
#include <sys/time.h>
#endif
namespace mold {
i64 Counter::get_value() {
return values.combine(std::plus());
}
void Counter::print() {
sort(instances, [](Counter *a, Counter *b) {
return a->get_value() > b->get_value();
});
for (Counter *c : instances)
std::cout << std::setw(20) << std::right << c->name
<< "=" << c->get_value() << "\n";
}
static i64 now_nsec() {
#ifdef _WIN32
return (i64)std::chrono::steady_clock::now().time_since_epoch().count();
#else
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (i64)t.tv_sec * 1'000'000'000 + t.tv_nsec;
#endif
}
static std::pair<i64, i64> get_usage() {
#ifdef _WIN32
auto to_nsec = [](FILETIME t) -> i64 {
return (((u64)t.dwHighDateTime << 32) + (u64)t.dwLowDateTime) * 100;
};
FILETIME creation, exit, kernel, user;
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
return {to_nsec(user), to_nsec(kernel)};
#else
auto to_nsec = [](struct timeval t) -> i64 {
return (i64)t.tv_sec * 1'000'000'000 + t.tv_usec * 1'000;
};
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
return {to_nsec(ru.ru_utime), to_nsec(ru.ru_stime)};
#endif
}
TimerRecord::TimerRecord(std::string name, TimerRecord *parent)
: name(name), parent(parent) {
start = now_nsec();
std::tie(user, sys) = get_usage();
if (parent)
parent->children.push_back(this);
}
void TimerRecord::stop() {
if (stopped)
return;
stopped = true;
i64 user2;
i64 sys2;
std::tie(user2, sys2) = get_usage();
end = now_nsec();
user = user2 - user;
sys = sys2 - sys;
}
static void print_rec(TimerRecord &rec, i64 indent) {
printf(" % 8.3f % 8.3f % 8.3f %s%s\n",
((double)rec.user / 1'000'000'000),
((double)rec.sys / 1'000'000'000),
(((double)rec.end - rec.start) / 1'000'000'000),
std::string(indent * 2, ' ').c_str(),
rec.name.c_str());
sort(rec.children, [](TimerRecord *a, TimerRecord *b) {
return a->start < b->start;
});
for (TimerRecord *child : rec.children)
print_rec(*child, indent + 1);
}
void print_timer_records(
tbb::concurrent_vector<std::unique_ptr<TimerRecord>> &records) {
for (i64 i = records.size() - 1; i >= 0; i--)
records[i]->stop();
for (i64 i = 0; i < records.size(); i++) {
TimerRecord &inner = *records[i];
if (inner.parent)
continue;
for (i64 j = i - 1; j >= 0; j--) {
TimerRecord &outer = *records[j];
if (outer.start <= inner.start && inner.end <= outer.end) {
inner.parent = &outer;
outer.children.push_back(&inner);
break;
}
}
}
std::cout << " User System Real Name\n";
for (std::unique_ptr<TimerRecord> &rec : records)
if (!rec->parent)
print_rec(*rec, 0);
std::cout << std::flush;
}
} // namespace mold
-115
View File
@@ -1,115 +0,0 @@
#include "common.h"
namespace mold {
// A tar file consists of one or more Ustar header followed by data.
// Each Ustar header represents a single file in an archive.
//
// tar is an old file format, and its `name` field is only 100 bytes long.
// If `name` is longer than 100 bytes, we can emit a PAX header before a
// Ustar header to store a long filename.
//
// For simplicity, we always emit a PAX header even for a short filename.
struct UstarHeader {
UstarHeader() {
memset(this, 0, sizeof(*this));
}
void finalize() {
memset(checksum, ' ', sizeof(checksum));
memcpy(magic, "ustar", 5);
memcpy(version, "00", 2);
// Compute checksum
int sum = 0;
for (i64 i = 0; i < sizeof(*this); i++)
sum += ((u8 *)this)[i];
// We need to convince the compiler that sum isn't too big to silence
// -Werror=format-truncation.
ASSUME(sum < 01'000'000);
snprintf(checksum, sizeof(checksum), "%06o", sum);
}
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char checksum[8];
char typeflag[1];
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char pad[12];
};
static_assert(sizeof(UstarHeader) == 512);
static std::string encode_path(std::string basedir, std::string path) {
path = path_clean(basedir + "/" + path);
// Construct a string which contains something like
// "16 path=foo/bar\n" where 16 is the size of the string
// including the size string itself.
i64 len = std::string(" path=\n").size() + path.size();
i64 total = std::to_string(len).size() + len;
total = std::to_string(total).size() + len;
return std::to_string(total) + " path=" + path + "\n";
}
std::unique_ptr<TarWriter>
TarWriter::open(std::string output_path, std::string basedir) {
FILE *out = fopen(output_path.c_str(), "w");
if (!out)
return nullptr;
return std::unique_ptr<TarWriter>(new TarWriter(out, basedir));
}
TarWriter::~TarWriter() {
fclose(out);
}
void TarWriter::append(std::string path, std::string_view data) {
// Write PAX header
static_assert(sizeof(UstarHeader) == BLOCK_SIZE);
UstarHeader pax;
std::string attr = encode_path(basedir, path);
snprintf(pax.size, sizeof(pax.size), "%011zo", attr.size());
pax.name[0] = '/';
pax.typeflag[0] = 'x';
pax.finalize();
fwrite(&pax, sizeof(pax), 1, out);
// Write pathname
fwrite(attr.data(), attr.size(), 1, out);
fseek(out, align_to(ftell(out), BLOCK_SIZE), SEEK_SET);
// Write Ustar header
UstarHeader ustar;
memcpy(ustar.mode, "0000664", 8);
snprintf(ustar.size, sizeof(ustar.size), "%011zo", data.size());
ustar.finalize();
fwrite(&ustar, sizeof(ustar), 1, out);
// Write file contents
fwrite(data.data(), data.size(), 1, out);
fseek(out, align_to(ftell(out), BLOCK_SIZE), SEEK_SET);
// A tar file must ends with two empty blocks, so write such
// terminator and seek back.
u8 terminator[BLOCK_SIZE * 2] = {};
fwrite(&terminator, BLOCK_SIZE * 2, 1, out);
fseek(out, -BLOCK_SIZE * 2, SEEK_END);
assert(ftell(out) % BLOCK_SIZE == 0);
}
} // namespace mold
-33
View File
@@ -1,33 +0,0 @@
# Get a git hash value. We do not want to use git command here
# because we don't want to make git a build-time dependency.
if(EXISTS "${SOURCE_DIR}/.git/HEAD")
file(READ "${SOURCE_DIR}/.git/HEAD" HASH)
string(STRIP "${HASH}" HASH)
if(HASH MATCHES "^ref: (.*)")
set(HEAD "${CMAKE_MATCH_1}")
if(EXISTS "${SOURCE_DIR}/.git/${HEAD}")
file(READ "${SOURCE_DIR}/.git/${HEAD}" HASH)
string(STRIP "${HASH}" HASH)
else()
file(READ "${SOURCE_DIR}/.git/packed-refs" PACKED_REFS)
string(REGEX REPLACE ".*\n([0-9a-f]+) ${HEAD}\n.*" "\\1" HASH "\n${PACKED_REFS}")
endif()
endif()
endif()
# Create new file contents and update a given file if necessary.
set(NEW_CONTENTS "#include <string>
namespace mold {
std::string mold_git_hash = \"${HASH}\";
}
")
if(EXISTS "${OUTPUT_FILE}")
file(READ "${OUTPUT_FILE}" OLD_CONTENTS)
if(NOT "${NEW_CONTENTS}" STREQUAL "${OLD_CONTENTS}")
file(WRITE "${OUTPUT_FILE}" "${NEW_CONTENTS}")
endif()
else()
file(WRITE "${OUTPUT_FILE}" "${NEW_CONTENTS}")
endif()
-20
View File
@@ -1,20 +0,0 @@
#include "common.h"
#include <random>
namespace mold {
std::array<u8, 16> get_uuid_v4() {
std::array<u8, 16> bytes;
std::random_device rand;
u32 buf[4] = { rand(), rand(), rand(), rand() };
memcpy(bytes.data(), buf, 16);
// Indicate that this is UUIDv4 as defined by RFC4122.
bytes[6] = (bytes[6] & 0b00001111) | 0b01000000;
bytes[8] = (bytes[8] & 0b00111111) | 0b10000000;
return bytes;
}
} // namespace mold
+153 -127
View File
@@ -1,172 +1,198 @@
#!/bin/bash
#
# This script creates a mold binary distribution. The output is written in
# this directory as `mold-$version-$arch-linux.tar.gz` (e.g.,
# `mold-1.0.3-x86_64-linux.tar.gz`).
# This script creates a mold binary distribution. The output is written to
# the `dist` directory as `mold-$version-$arch-linux.tar.gz` (e.g.
# `mold-2.42.0-x86_64-linux.tar.gz`).
#
# The mold executable created by this script is statically linked to
# libstdc++ but dynamically linked to libc, libm, libz, and librt, as
# these libraries almost always exist on any Linux system. We can't
# statically link libc because doing so would disable dlopen(), which is
# necessary to open the LTO linker plugin.
#
# This script aims to produce reproducible outputs. That means if you run
# the script twice on the same git commit, it should produce bit-by-bit
# identical binary files. This property is crucial as a countermeasure
# against supply chain attacks. With this, you can verify that the binary
# files distributed on the GitHub release pages are created from the
# commit with release tags by rebuilding the binaries yourself.
# This script aims to produce reproducible outputs. The container images,
# Rust toolchain, Cargo dependencies and file timestamps are pinned so that
# the same git commit can produce a bit-for-bit identical binary file. This
# property serves as a strong safeguard against supply chain attacks. With
# a reproducible build, anyone can independently verify that the binary
# files published on our GitHub release page were built from the git commit
# tagged for release by rebuilding the binaries themselves.
#
# Debian provides snapshot.debian.org to host all historical binary
# packages. We use it to construct Docker images pinned to a
# particular timestamp.
# packages. Distro package repositories must be pinned as well as the base
# image before a build is fully reproducible. The loongarch64 build still
# uses the live Debian sid repository, so it does not yet have that property.
#
# We aim to use a reasonably old Debian version because we'll dynamically
# link glibc to mold, and a binary linked against a newer version of glibc
# won't work on a system with an older version of glibc.
# The mold executable created by this script is dynamically linked to the
# system C runtime and other standard system libraries. We can't statically
# link glibc because doing so would disable dlopen(), which is required to
# load the LTO linker plugin.
#
# We need GCC 10 or newer to build mold. If GCC 10 is not available on an
# old Debian version, we'll build it ourselves.
# We use a reasonably old Debian version for the build environment because
# a binary dynamically linked against a newer version of glibc won't work
# on a system with an older version of glibc.
#
# You may need to run the following command to use Docker with Qemu:
# The Rust toolchain is downloaded from the official Rust distribution
# site. Its version and SHA-256 hash are recorded below, so the toolchain
# is another pinned build input.
#
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# This script can be used to create non-native binaries (e.g., building
# aarch64 binary on x86-64) because Podman automatically runs everything
# under QEMU if the container image is not native. To use this script for
# non-native builds, you may need to install the qemu-user-static package.
set -e -x
cd "$(dirname $0)"
cd "$(dirname "$0")"
# The executable embeds the hash of the Git commit it is built from, and
# the commit's timestamp is used as the file timestamp, so this script
# must run in a Git clone. A worktree doesn't work either because its
# `.git` file refers to a directory outside the build container.
if [ ! -d .git ]; then
echo "$0: must be run in a Git clone of mold" >&2
exit 1
fi
usage() {
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x | loongarch64 ]"
exit 1
}
case $# in
0)
arch=$(uname -m)
if [ $arch = arm64 ]; then
if [ "$arch" = arm64 ]; then
arch=aarch64
elif [[ $arch = arm* ]]; then
elif [[ "$arch" = arm* ]]; then
arch=arm
fi
;;
1)
arch="$1"
arch=$1
;;
*)
usage
;;
esac
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || usage
rust_version=1.97.1
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' CMakeLists.txt)
dest=mold-$version-$arch-linux
# Switch to the pinned snapshot.debian.org sources that the Debian images
# ship commented out; the live mirrors no longer carry every architecture.
apt_setup="sed -i -e '/^deb/d' -e 's/^# deb /deb /g' /etc/apt/sources.list"
if [ "$GITHUB_REPOSITORY" = '' ]; then
image=mold-builder-$arch
docker_build="docker build --platform linux/$arch -t $image -"
else
# If this script is running on GitHub Actions, we want to cache
# the created Docker image in GitHub's Docker repostiory.
image=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch
docker_build="docker buildx build --platform linux/$arch -t $image --push --cache-to type=inline --cache-from type=registry,ref=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch -"
fi
# Create a Docker image.
case $arch in
x86_64)
# Debian 8 (Jessie) released in April 2015
cat <<EOF | $docker_build
FROM debian:jessie-20210326@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends wget bzip2 file make autoconf gcc g++ libssl-dev && \
rm -rf /var/lib/apt/lists
# Build CMake 3.27
RUN mkdir -p /build/cmake && \
cd /build/cmake && \
wget -O- --no-check-certificate https://cmake.org/files/v3.27/cmake-3.27.7.tar.gz | tar xzf - --strip-components=1 && \
./bootstrap --parallel=\$(nproc) && \
make -j\$(nproc) && \
make install && \
rm -rf /build
# Build GCC 10
RUN mkdir -p /build/gcc && \
cd /build/gcc && \
wget -O- http://ftp.gnu.org/gnu/gcc/gcc-10.5.0/gcc-10.5.0.tar.gz | tar xzf - --strip-components=1 && \
mkdir isl gmp mpc mpfr && \
wget -O- http://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 | tar xjf - --strip-components=1 -C isl && \
wget -O- http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.bz2 | tar xjf - --strip-components=1 -C gmp && \
wget -O- http://ftp.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz | tar xzf - --strip-components=1 -C mpc && \
wget -O- http://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.gz | tar xzf - --strip-components=1 -C mpfr && \
./configure --prefix=/usr --enable-languages=c,c++ --disable-bootstrap --disable-multilib && \
make -j\$(nproc) && \
make install && \
ln -sf /usr/lib64/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && \
rm -rf /build
EOF
;;
aarch64 | arm | ppc64le | s390x)
# Debian 10 (Bullseye) released in July 2019
# Debian 9 (Stretch) released in June 2017.
#
# We don't want to build GCC for these targets with Qemu becuase
# that'd take extremely long time. Also I believe old build machines
# are usually x86-64.
[ $arch = aarch64 ] && digest=d5ed76c5265576982e6599b6f12392290d9b52b315b19b28b640aaba6e8af002
[ $arch = arm ] && digest=bede2623dae269454c5b6dd4af15a10810a5f4ef75963d4eb6531628f98bd633
[ $arch = ppc64le ] && digest=255f385e735469493b3465befad59a16f9d46f41d0b50e4fa6d5928c5ee7702a
[ $arch = s390x ] && digest=96fb9ce5d3ce7f3dab7c34c18edfee093904cbc7fc19162dbcca22b2cc273b9d
cat <<EOF | $docker_build
FROM debian:bullseye-20231030@sha256:$digest
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
ln -sf /usr/bin/gcc-10 /usr/bin/cc && \
ln -sf /usr/bin/g++-10 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
# We use a Google-provided mirror (mirror.gcr.io) instead of the official
# Docker Hub (docker.io) because docker.io has a strict rate limit policy.
base_image=mirror.gcr.io/library/debian:stretch@sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be
rust_target=x86_64-unknown-linux-gnu
rust_sha256=b4cdbc7cc6b0ee0a2666b1872769fdb2ad8393b28b63952f6493b4b400e4832b
;;
aarch64)
# Debian 11 (Bullseye) released in August 2021.
base_image=mirror.gcr.io/library/debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
rust_target=aarch64-unknown-linux-gnu
rust_sha256=2f2496c70bd336a66a4c8baf2d303ba161f3552f192444c3639ba903c7c1e2c5
;;
arm)
# Debian 11 (Bullseye) released in August 2021.
base_image=mirror.gcr.io/library/debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
rust_target=armv7-unknown-linux-gnueabihf
rust_sha256=e89c5e33aaddc6ef56857000c9117875c2997e9a1a500bd7b16277c9874b002f
;;
riscv64)
# snapshot.debian.org is not available for RISC-V binaries
cat <<EOF | $docker_build
FROM riscv64/debian:unstable-20231030@sha256:be1882409392c1f68f23e1e04bd965c49398f4a358b1a15f7b1820c0b39ede5b
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential gcc-12 g++-12 cmake && \
ln -sf /usr/bin/gcc-12 /usr/bin/cc && \
ln -sf /usr/bin/g++-12 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
base_image=mirror.gcr.io/riscv64/debian:unstable-20240926@sha256:25654919c2926f38952cdd14b3300d83d13f2d820715f78c9f4b7a1d9399bf48
apt_setup="sed -i -e '/^URIs/d' -e 's/^# http/URIs: http/' /etc/apt/sources.list.d/debian.sources"
rust_target=riscv64gc-unknown-linux-gnu
rust_sha256=59bec35d8febb2ab918fa41cffbaa5b07146a63bdc33f029ff756d70a3151ece
;;
ppc64le)
# Debian 11 (Bullseye) released in August 2021.
base_image=mirror.gcr.io/library/debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
rust_target=powerpc64le-unknown-linux-gnu
rust_sha256=ff524eef5a59d801df09ccad5cdaf9ea1f0a07d75cbed2a7e9f013a9eb76a3c1
;;
s390x)
# Debian 11 (Bullseye) released in August 2021.
base_image=mirror.gcr.io/library/debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
rust_target=s390x-unknown-linux-gnu
rust_sha256=808268af9e880d41b8cb32b242e38c9bd3ea7aba6409b02fbffa0fbc5370c538
;;
loongarch64)
base_image=mirror.gcr.io/loongarch64/debian:sid@sha256:0356df4e494bbb86bb469377a00789a5b42bbf67d5ff649a3f9721b745cbef77
apt_setup="echo 'deb http://deb.debian.org/debian sid main' > /etc/apt/sources.list"
rust_target=loongarch64-unknown-linux-gnu
rust_sha256=d5a925962854730ae7641420d8337af93988ea4ff47b503a856ec53776c87841
;;
*)
usage
;;
esac
# Source tarballs available on GitHub don't contain .git history.
# Clone the repo if missing.
[ -d .git ] || git clone --branch v$version --depth 1 --bare https://github.com/rui314/mold .git
# Create a Podman image containing the native C tools and a pinned Rust
# toolchain. The downloaded archive is checked before it is unpacked.
image=mold-rust-builder-$arch
archive=rust-$rust_version-$rust_target.tar.gz
podman build --arch "$arch" -t "$image" - <<EOF
FROM $base_image
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN $apt_setup && \
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential ca-certificates git wget && \
rm -rf /var/lib/apt/lists
RUN mkdir /tmp/rust && \
cd /tmp/rust && \
wget --progress=dot:mega https://static.rust-lang.org/dist/$archive && \
echo '$rust_sha256 $archive' | sha256sum -c && \
tar xf $archive && \
./${archive%.tar.gz}/install.sh --prefix=/usr/local --disable-ldconfig && \
cd / && \
rm -rf /tmp/rust
EOF
version=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml)
dest=mold-$version-$arch-linux
# We use the timestamp of the last Git commit as the file timestamp
# for build artifacts.
timestamp="$(git log -1 --format=%ci)"
timestamp=$(git log -1 --format=%ct)
# `uname -m` in an ARM32 container running on an ARM64 host reports it
# not as ARM32 but as ARM64. Keep the reported machine consistent with
# the ARM32 userspace for any build tool that inspects it.
setarch=
[ "$arch" = arm ] && setarch='setarch linux32'
mkdir -p dist "target/dist-vendor-$arch"
# Cargo verifies registry packages against the checksums in Cargo.lock and
# checks out Git dependencies at the commit recorded there. Vendor them in a
# separate networked step so that the actual build can run without a network.
# Cargo's own cache is a tmpfs because the libgit2 inside a 32-bit cargo can't
# read a bind-mounted ext4 directory (readdir fails with EOVERFLOW under QEMU).
podman run --arch "$arch" -it --rm --userns=host --pids-limit=-1 \
--pull=never --env CARGO_HOME=/cargo --tmpfs /cargo -v "$(pwd):/mold:ro" \
-v "$(pwd)/target/dist-vendor-$arch:/vendor" "$image" $setarch \
bash -c 'cd /mold && cargo vendor --locked /vendor/sources > /vendor/config.toml'
# Build mold in a container.
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" $image bash -c "
#
# SOURCE_DATE_EPOCH is a standardized environment variable that allows
# build artifacts to appear as if they were built at a specific time.
# Fixed source, vendor and target paths keep embedded build paths stable.
podman run --arch "$arch" -it --rm --userns=host --pids-limit=-1 --network=none \
--pull=never --env SOURCE_DATE_EPOCH="$timestamp" --env DEST="$dest" \
-v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" \
-v "$(pwd)/target/dist-vendor-$arch:/vendor:ro" "$image" \
$setarch bash -c '
set -e
mkdir -p /build/mold
cd /build/mold
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=On /mold
cmake --build . -j\$(nproc)
ctest -j\$(nproc)
cmake --install . --prefix $dest --strip
find $dest -print | xargs touch --no-dereference --date='$timestamp'
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /mold/$dest.tar.gz
chown $(id -u):$(id -g) /mold/$dest.tar.gz
cp mold /mold
"
which sha256sum > /dev/null && sha256sum $dest.tar.gz
export CARGO_TARGET_DIR=/build/target
cd /mold
cargo build --release --frozen --config /vendor/config.toml --package mold-cli
stage=/build/$DEST
DESTDIR=/build PREFIX=/$DEST ./install-mold.sh
strip --strip-unneeded "$stage/bin/mold" "$stage/lib/mold/mold-wrapper.so"
find "$stage" -print | xargs touch --no-dereference --date="@$SOURCE_DATE_EPOCH"
cd /build
find "$DEST" -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > "/dist/$DEST.tar.gz"
sha256sum "/dist/$DEST.tar.gz"
'
-150
View File
@@ -1,150 +0,0 @@
This is a note about interesting bugs that I met during the
development of the mold linker.
## GNU IFUNC
Problem: A statically-linked "hello world" program mysteriously
crashed in `__libc_start_main` function which is called just after
`_start`.
Investigation: I opened up gdb and found that the program reads a
bogus value from some array. It looks like `memcpy` failed to copy
proper data there. After some investigation, I noticed that `memcpy`
did't copy data at all but instead returned the address of
`__memcpy_avx_unaligned` function, which is a real `memcpy` function
optimized for machines with the AVX registers.
This odd issue was caused by the GNU IFUNC mechanism. That is, if a
function symbol has type `STT_GNU_IFUNC`, the function does not do
what its name suggests to do but instead returns a pointer to a
function that does the actual job. In this case, `memcpy` is an IFUNC
function, and it returns an address of `__memcpy_avx_unaligned` which
is a real `memcpy` function.
IFUNC function addresses are stored to `.got` section in an ELF
executable. The dynamic loader executes all IFUNC functions at
startup to replace their GOT entries with their return values. This
mechanism allows programs to choose the best implementation among
variants of the same function at runtime based on the machine info.
If a program is statically-linked, there's no dynamic loader that
rewrites the GOT entries. Therefore, if a program is
statically-linked, a libc's startup routine does that on behalf of the
dynamic loader. Concretely, a startup routine interprets all dynamic
relocations between `__rela_iplt_start` and `__rela_iplt_stop`
symbols. It is linker's responsibility to emit dynamic relocations
for IFUNC symbols even if it is linking a statically-linked program
and mark the beginning and the ending of a `.rela.dyn` section with
the symbols, so that the startup routine can find the relocations.
The bug was my linker didn't define `__rela_iplt_start` and
`__rela_iplt_stop` symbols. Since these symbols are weak, they are
initialized to zero. From the point of the initializer function,
there's no dynamic relocations between `__rela_iplt_start` and
`__rela_iplt_stop` symbols. That left GOT entries for IFUNC symbols
untouched.
The proper fix was to emit dynamic relocations for IFUNC symbols and
define the linker-synthesized symbols. I did that, and the bug was
fixed.
## stdio buffering
Problem: A statically-linked "Hello world" program prints out the
message if executed as `./hello`, but it doesn't output anything if
executed as `./hello | cat`.
Investigation: I knew that the default buffering mode for stdout is
line buffering (buffer is flushed on every '\n'), but if it is not
connected to the terminal (i.e. `isatty(2)` returns 0 on
`STDOUT_FILENO`), it automatically switches to full buffering (buffer
is flushed when it becomes full). So, it looks like libc failed to
flush the stdout on program exit for some reason.
I traced all function calls using gdb and noticed that `__libc_atexit`
was not called. That function seemed to be responsible for buffer
flushing. I don't know how exactly I found the root cause, but after
spending an hour or two, I found that `__start___libc_atexit` and
`__stop___libc_atexit` have value 0 in my linker's output while they
mark a section containing the address of `__libc_atexit` in GNU ld's
output.
So, libc doesn't directly call `__libc_atexit` but instead call all
function pointers between `__start___libc_atexit` and
`__stop___libc_atexit` symbols. libc puts `__libc_atexit` address in
`_libc_atexit` section, expecting that the linker automatically
creates the start and the end marker symbols for the section.
There's an obscure linker feature: if a section name is valid as a C
identifier (e.g. `foo` or `_foo_bar` but not `.foo`), the linker
automatically creates marker symbols by prepending `__start_` and
`__stop_` to the section name. My linker lacked the feature.
I implemented the feature, and the bug was fixed.
## TLS variable initialization
Problem: A statically-linked "hello world" program crashes after
reading a thread-local variable.
Investigation: Thread-local variables are very different from other
types of varaibles because there may be more than one instance of the
same variable in memory. Each thread has its copy of thread-local
varaibles. `%fs` segment register points the end of the variable area
for the current thread, and the variables are accessed as an offset
from `%fs`.
Thread-local variables may be initialized (e.g. `thread_local int x =
5;`). The linker gathers all thread-local variables and put them into
`PT_TLS` segment. At runtime, the contents of the segment is used as
an "initialization image" for new threads. When a new thread is
created, the image is memcpy'ed to the new thread's thread-local
variable area. The initialization image itself is read-only at
runtime.
It took more than a day to find out that memcpy copies the
initialization image to a different place than the thread-local
variables reside. That means, thread-local variables had garbage as
initial values, and the program crashed when using them.
The problem is that I set a very large value (4096) to the alignment
of `PT_TLS` segment. All `PT_LOAD` segments are naturally aligned to
the page boundary, so I use the same value for `PT_TLS`, but that was
a mistake. When a thread initialization routine sets a value to `%fs`,
it first aligns the end of the thread-local variable area address to
`PT_TLS` alignment value. So, if you set a large value to `PT_TLS`
alignment, `%fs` is set to a wrong place.
I fixed `PT_TLS` alignment, and the bug was gone.
## stdio buffering (another issue)
I noticed that a dynamically-linked "hello world" program didn't
flush its stdout buffer on exit. The cause of the problem was that the
executable had more than one DT_NEEDED entry for `libc.so`.
DT_NEEDED entries in `.dynamic` section specify a list of shared
object file names which need to be linked at runtime. I added one
DT_NEEDED entry for each library specified with the `-l` option.
The pitfall is, unlike object files, libraries are allowed to
appear more than once in a command line, and the linker has to
de-duplicate them before processing. Adding more than one DT_NEEDED
entry for the same shared object causes mysterious issues like this.
# Copy relocations and symbol aliases
environ, _environ and __environ point to the same location in libc.so,
so when we create a copy relocation for one of the symbols, we need to
do that for all of them. Otherwise, they'll end up pointing to different
places which causes a very mysterious issue.
# DT_DEBUG and gdb
If you forget to add an entry with type DT_DEBUG to .dynamic, gdb's
`info sharedlibrary` command doesn't print out a list of shared
libraries loaded to memory. The value of the entry doesn't matter, so
it can be just zero. The existence of it is important.
#
__EH_FRAME_BEGIN__ in libgcc/crtstuff.c
-1
View File
@@ -1 +0,0 @@
<svg version="1.1" viewBox="0.0 0.0 600.0 371.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" width="600" height="371" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M0 0L600.0 0L600.0 371.0L0 371.0L0 0Z" fill-rule="nonzero"/><path stroke="#333333" stroke-width="1.0" stroke-linecap="butt" d="M82.5 275.5L581.5 275.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 231.5L581.5 231.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 186.5L581.5 186.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 142.5L581.5 142.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 98.5L581.5 98.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 53.5L581.5 53.5" fill-rule="nonzero"/><clipPath id="id_0"><path d="M82.55 53.916668L581.45 53.916668L581.45 275.45L82.55 275.45L82.55 53.916668Z" clip-rule="nonzero"/></clipPath><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M158.0 275.0L136.0 275.0L136.0 229.0C136.0 227.89543 136.89543 227.0 138.0 227.0L156.0 227.0C157.10457 227.0 158.0 227.89543 158.0 229.0Z" fill-rule="nonzero"/><path fill="#4285f4" clip-path="url(#id_0)" d="M158.0 275.0L136.0 275.0L136.0 229.0C136.0 227.89543 136.89543 227.0 138.0 227.0L156.0 227.0C157.10457 227.0 158.0 227.89543 158.0 229.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M307.0 275.0L285.0 275.0L285.0 91.0C285.0 89.89543 285.89542 89.0 287.0 89.0L305.0 89.0C306.10458 89.0 307.0 89.89543 307.0 91.0Z" fill-rule="nonzero"/><path fill="#4285f4" clip-path="url(#id_0)" d="M307.0 275.0L285.0 275.0L285.0 91.0C285.0 89.89543 285.89542 89.0 287.0 89.0L305.0 89.0C306.10458 89.0 307.0 89.89543 307.0 91.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M182.0 275.0L160.0 275.0L160.0 244.0C160.0 242.89543 160.89543 242.0 162.0 242.0L180.0 242.0C181.10457 242.0 182.0 242.89543 182.0 244.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M182.0 275.0L160.0 275.0L160.0 244.0C160.0 242.89543 160.89543 242.0 162.0 242.0L180.0 242.0C181.10457 242.0 182.0 242.89543 182.0 244.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M331.0 275.0L309.0 275.0L309.0 131.0C309.0 129.89543 309.89542 129.0 311.0 129.0L329.0 129.0C330.10458 129.0 331.0 129.89543 331.0 131.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M331.0 275.0L309.0 275.0L309.0 131.0C309.0 129.89543 309.89542 129.0 311.0 129.0L329.0 129.0C330.10458 129.0 331.0 129.89543 331.0 131.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M480.0 275.0L458.0 275.0L458.0 156.0C458.0 154.89543 458.89542 154.0 460.0 154.0L478.0 154.0C479.10458 154.0 480.0 154.89543 480.0 156.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M480.0 275.0L458.0 275.0L458.0 156.0C458.0 154.89543 458.89542 154.0 460.0 154.0L478.0 154.0C479.10458 154.0 480.0 154.89543 480.0 156.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M206.0 275.0L184.0 275.0L184.0 270.0C184.0 268.89542 184.89543 268.0 186.0 268.0L204.0 268.0C205.10457 268.0 206.0 268.89542 206.0 270.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M206.0 275.0L184.0 275.0L184.0 270.0C184.0 268.89542 184.89543 268.0 186.0 268.0L204.0 268.0C205.10457 268.0 206.0 268.89542 206.0 270.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M355.0 275.0L333.0 275.0L333.0 254.0C333.0 252.89543 333.89542 252.0 335.0 252.0L353.0 252.0C354.10458 252.0 355.0 252.89543 355.0 254.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M355.0 275.0L333.0 275.0L333.0 254.0C333.0 252.89543 333.89542 252.0 335.0 252.0L353.0 252.0C354.10458 252.0 355.0 252.89543 355.0 254.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M504.0 275.0L482.0 275.0L482.0 250.0C482.0 248.89543 482.89542 248.0 484.0 248.0L502.0 248.0C503.10458 248.0 504.0 248.89543 504.0 250.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M504.0 275.0L482.0 275.0L482.0 250.0C482.0 248.89543 482.89542 248.0 484.0 248.0L502.0 248.0C503.10458 248.0 504.0 248.89543 504.0 250.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroLine truncated

Before

Width:  |  Height:  |  Size: 99 KiB

-46
View File
@@ -1,46 +0,0 @@
# Coding Guidelines
mold is written in C++20, but as is the case with every C++ project,
it has local coding rules. In this document, I'll explain some of them
and try to give justifications for why I chose such rules.
## DOs
- Always use `i64` (which is a type alias for `int64_t` in mold) for
integers unless you have a reason not to. For example, even if you know
that a loop counter won't exceed 100, you should stop thinking about it
and just use `i64`.
Justification: Local variables are usually on CPU registers, so on
64-bit CPUs, there's no performance peanlty on choosing `i64` over
`i32`. Even if a compiler has to spill register values to the stack,
I don't think there's an observable difference between `i32` and
`i64`. Therefore, extra 32 bits are essentially free. On 32-bit CPUs,
they are not free, but that's OK because we are writing mold for modern
computers. mold will still run on 32-bit computers but a bit slowly.
By always using `i64`, we can eliminate the need to think about the
"right" size for each variable. It also reduces the risk of integer
overflow.
Exceptions: If you have to allocate a very large number (e.g. millions)
of the same object, its size matters. In that case, use a smaller type.
## DON'Ts
- Don't use `auto` unless its actual type is obvious in the very narrow
context. Currently, we use `auto` only for lambdas.
Justification: I think `auto` makes code writing easier but code reading
harder, because readers have to make a guess as to what is the actual
type of `auto`. If you are already familiar with the existing codebase,
you may be able to guess it easily, but that's not always the case.
I want to keep the mold codebase friendly to first-time readers.
- Don't over-use inheritance. In mold, most classes don't have parents,
and even if they do, their class hierarchy is very shallow. Currently
its height is just two (i.e. abstract classes and their implementations).
Justification: Designing class hierarchies is fun as it feels like
taxonomy, but I don't think that always help writing code. It looks like
simpler class hierarchy makes its code simpler.
-486
View File
@@ -1,486 +0,0 @@
## Design and implementation of mold
For the rest of this documentation, I'll explain the design and the
implementation of mold. If you are only interested in using mold, you
don't need to read the below.
### Motivation
Here is why I'm writing a new linker:
- Even though lld has significantly improved the situation, linking is
still one of the slowest steps in a build. It is especially
annoying when I changed one line of code and had to wait for a few
seconds or even more for a linker to complete. It should be
instantaneous. There's a need for a faster linker.
- The number of cores on a PC has increased a lot lately, and this
trend is expected to continue. However, the existing linkers can't
take the advantage of the trend because they don't scale well for more
cores. I have a 64-core/128-thread machine, so my goal is to create
a linker that uses the CPU nicely. mold should be much faster than
other linkers on 4 or 8-core machines too, though.
- It looks to me that the designs of the existing linkers are somewhat
too similar, and I believe there are a lot of drastically different
designs that haven't been explored yet. Developers generally don't
care about linkers as long as they work correctly, and they don't
even think about creating a new one. So there may be lots of low
hanging fruits there in this area.
### Basic design
- In order to achieve a `cp`-like performance, the most important
thing is to fix the layout of an output file as quickly as possible, so
that we can start copying actual data from input object files to an
output file as soon as possible.
- Copying data from input files to an output file is I/O-bounded, so
there should be room for doing computationally-intensive tasks while
copying data from one file to another.
- We should allow the linker to preload object files from disk and
parse them in memory before a complete set of input object files
is ready. To do so, we need
to split the linker into two in such a way that the latter half of
the process finishes as quickly as possible by speculatively parsing
and preprocessing input files in the first half of the process.
- One of the most computationally-intensive stage among linker stages
is symbol resolution. To resolve symbols, we basically have to throw
all symbol strings into a hash table to match undefined symbols with
defined symbols. But this can be done in the preloading stage using
[string interning](https://en.wikipedia.org/wiki/String_interning).
- Object files may contain a special section called a mergeable string
section. The section contains lots of null-terminated strings, and
the linker is expected to gather all mergeable string sections and
merge their contents. So, if two object files contain the same
string literal, for example, the resulting output will contain a
single merged string. This step is computationally intensive, but string
merging can be done in the preloading stage using string interning.
- Static archives (.a files) contain object files, but the static
archive's string table contains only defined symbols of member
object files and lacks other types of symbols. That makes static
archives unsuitable for speculative parsing. Therefore, the linker
should ignore the symbol table of static archive and directly read
static archive members.
- If there's a relocation that uses a GOT of a symbol, then we have to
create a GOT entry for that symbol. Otherwise, we shouldn't. That
means we need to scan all relocation tables to fix the length and
the contents of a .got section. This is computationally intensive,
but this step is parallelizable.
### Linker Script
Linker script is an embedded language for the linker. It is mainly
used to control how input sections are mapped to output sections and
the layout of the output, but it can also do a lot of tricky stuff.
Its feature is useful especially for embedded programming, but it's
also an awfully underdocumented and complex language.
We have to implement a subset of the linker script language anwyay,
because on Linux, /usr/lib/x86_64-linux-gnu/libc.so is (despite its
name) not a shared object file but actually an ASCII file containing
linker script code to load the _actual_ libc.so file. But the feature
set for this purpose is very limited, and it is okay to implement them
to mold.
Besides that, we really don't want to implement the linker script
language. But at the same time, we want to satisfy the user needs that
are currently satisfied with the linker script language. So, what
should we do? Here is my observation:
- Linker script allows doing a lot of tricky stuff, such as specifying
the exact layout of a file, inserting arbitrary bytes between
sections, etc. But most of them can be done with a post-link binary
editing tool (such as `objcopy`).
- It looks like there are two things that truly cannot be done by a
post-link editing tool: (a) mapping input sections to output
sections, and (b) applying relocations.
From the above observation, I believe we need to provide only the
following features instead of the entire linker script language:
- A method to specify how input sections are mapped to output
sections, and
- a method to set addresses to output sections, so that relocations
are applied based on desired addresses.
I believe everything else can be done with a post-link binary editing
tool.
### Details
- As we aim to the 1-second goal for Chromium, every millisecond
counts. We can't ignore the latency of process exit. If we mmap a
lot of files, \_exit(2) is not instantaneous but takes a few hundred
milliseconds because the kernel has to clean up a lot of
resources. As a workaround, we should organize the linker command as
two processes; the first process forks the second process, and the
second process does the actual work. As soon as the second process
writes a result file to a filesystem, it notifies the first process,
and the first process exits. The second process can take time to
exit, because it is not an interactive process.
- At least on Linux, it looks like the filesystem's performance to
allocate new blocks to a new file is the limiting factor when
creating a new large file and filling its contents using mmap.
If you already have a large file in the buffer cache, writing to it is
much faster than creating a new fresh file and writing to it.
Based on this observation, mold overwrites to an existing
executable file if exists. My quick benchmark showed that I could
save 300 milliseconds when creating a 2 GiB output file.
Linux doesn't allow to open an executable for writing if it is
running (you'll get a "text busy" error if you attempt). mold
falls back to the usual way if it fails to open an output file.
- The output from the linker should be deterministic for the sake of
[build reproducibility](https://en.wikipedia.org/wiki/Reproducible_builds)
and ease of debugging. This might add a little bit of overhead to
the linker, but that shouldn't be too much.
- A .build-id, a unique ID embedded to an output file, is usually
computed by applying a cryptographic hash function (e.g. SHA-1) to
an output file. This is a slow step, but we can speed it up by
splitting a file into small chunks, computing SHA-1 for each chunk,
and then computing SHA-1 of the concatenated SHA-1 hashes
(i.e. constructing a [Merkle
Tree](https://en.wikipedia.org/wiki/Merkle_tree) of height 2).
Modern x86 processors have purpose-built instructions for SHA-1 and
can compute SHA-1 pretty quickly at about 2 GiB/s. Using 16
cores, a build-id for a 2 GiB executable can be computed in 60 to 70
milliseconds.
- BFD, gold, and lld support section garbage collection. That is, a
linker runs a mark-sweep garbage collection on an input graph, where
sections are vertices and relocations are edges, to discard all
sections that are not reachable from the entry point symbol
(i.e. `_start`) or a few other root sections. In mold, we are using
multiple threads to mark sections concurrently.
- Similarly, BFD, gold an lld support Identical Comdat Folding (ICF)
as yet another size optimization. ICF merges two or more read-only
sections that happen to have the same contents and relocations.
To do that, we have to find isomorphic subgraphs from larger graphs.
I implemented a new algorithm for mold, which is 5x faster than lld
to do ICF for Chromium (from 5 seconds to 1 second).
- [Intel Threading Building
Blocks](https://github.com/oneapi-src/oneTBB) (TBB) is a good
library for parallel execution and has several concurrent
containers. We are particularly interested in using
`parallel_for_each` and `concurrent_hash_map`.
- TBB provides `tbbmalloc` which works better for multi-threaded
applications than the glib'c malloc, but it looks like
[jemalloc](https://github.com/jemalloc/jemalloc) and
[mimalloc](https://github.com/microsoft/mimalloc) are a little bit
more scalable than `tbbmalloc`.
### Size of the problem
When linking Chrome, a linker reads 3,430,966,844 bytes of data in
total. The data contains the following items:
| Data item | Number
| ------------------------ | ------
| Object files | 30,723
| Public undefined symbols | 1,428,149
| Mergeable strings | 1,579,996
| Comdat groups | 9,914,510
| Regular sections¹ | 10,345,314
| Public defined symbols | 10,512,135
| Symbols | 23,953,607
| Sections | 27,543,225
| Relocations against SHF_ALLOC sections | 39,496,375
| Relocations | 62,024,719
¹ Sections that have to be copied from input object files to an
output file. Sections that contain relocations or symbols are for
example excluded.
### Internals
In this section, I'll explain the internals of mold linker.
#### A brief history of Unix and the Unix linker
Conceptually, what a linker does is pretty simple. A compiler compiles
a fragment of a program (a single source file) into a fragment of
machine code and data (an object file, which typically has the .o
extension), and a linker stitches them together into a single
executable or a shared library image.
In reality, modern linkers for Unix-like systems are much more
complicated than the naive understanding because they have gradually
gained one feature at a time over the 50 years history of Unix, and
they are now something like a bag of lots of miscellaneous features in
which none of the features is more important than the others. It is
very easy to miss the forest for the trees, since for those who don't
know the details of the Unix linker, it is not clear which feature is
essential and which is not.
That being said, one thing is clear that at any point of Unix history,
a Unix linker has a coherent feature set for the Unix of that age. So,
let me entangle the history to see how the operating system, runtime,
and linker have gained features that we see today. That should give
you an idea of why a particular feature has been added to a linker in the
first place.
1. Original Unix didn't support shared libraries, and a program was
always loaded to a fixed address. An executable was something like
a memory dump that was just loaded to a particular address by the
kernel. After loading, the kernel started executing the program by
setting the instruction pointer to a particular address.
The most essential feature for any linker is relocation processing.
The original Unix linker of course supported that. Let me explain
what that is.
Individual object files are inevitably incomplete as a program,
because when a compiler created them, it only see a part of an
entire program. For example, if an object file contains a function
call that refers to another object file, the `call` instruction in the
object cannot be complete, as the compiler has no idea as to what
is the called function's address. To deal with this, the compiler
emits a placeholder value (typically just zero) instead of a real
address and leaves metadata in an object file saying "fix offset X
of this file with an address of Y". That metadata is called
"relocation". Relocations are typically processed by the linker.
It is easy for a linker to apply relocations for the original Unix
because a program is always loaded to a fixed address. It exactly
knows the addresses of all functions and data when linking a
program.
Static library support, which is still an important feature of Unix
linker, also dates back to this early period of Unix history.
To understand what it is, imagine that you are trying to compile
a program for the early Unix. You don't want to waste time to
compile libc functions every time you compile your program (the
computers of the era were incredibly slow), so you have already
placed each libc function into a separate source file and compiled
them individually. That means you have object files for each libc
function, e.g., printf.o, scanf.o, atoi.o, write.o, etc.
Given this configuration, all you have to do to link your program
against libc functions is to pick up the right set of libc object
files and give them to the linker along with the object files of your
program. But, keeping the linker command line in sync with the
libc functions you are using in your program is bothersome. You can
be conservative; you can specify all libc object files to the
command line, but that leads to program bloat because the linker
unconditionally link all object files given to it no matter whether
they are used or not. So, a new feature was added to the linker to
fix the problem. That is the static library, which is also called
the archive file.
An archive file is just a bundle of object files, just like zip
file but in an uncompressed form. An archive file typically has the
.a file extension and named after its contents. For example, the
archive file containing all libc objects is named `libc.a`.
If you pass an archive file along with other object files to the
linker, the linker pulls out an object file from the archive _only
when_ it is referenced by other object files. In other words,
unlike object files directly given to a linker, object files
wrapped in an archive are not linked to the output by default.
An archive works as a supplement to complete your program.
Even today, you can still find a libc archive file. Run `ar t
/usr/lib/x86_64-linux-gnu/libc.a` on Linux should give you a list
of object files in the libc archive.
2. In the '80s, Sun Microsystems, a leading commercial Unix vendor at the
time, added shared library support to their Unix variant, SunOS.
(This section is incomplete.)
### Concurrency strategy
In this section, I'll explain the high-level concurrency strategy of
mold.
In most places, mold adopts data parallelism. That is, we have a huge
number of pieces of data of the same kind, and we process each of them
individually using parallel for-loop. For example, after identifying
the exact set of input object files, we need to scan all relocation
tables to determine the sizes of .got and .plt sections. We do that
using a parallel for-loop. The granularity of parallel processing in
this case is the relocation table.
Data parallelism is very efficient and scalable because there's no
need for threads to communicate with each other while working on each
element of data. In addition to that, data parallelism is easy to
understand, as it is just a for-loop in which multiple iterations may
be executed in parallel. We don't use high-level communication or
synchronization mechanisms such as channels, futures, promises,
latches or something like that in mold.
In some cases, we need to share a little bit of data between threads
while executing a parallel for-loop. For example, the loop to scan
relocations turns on "requires GOT" or "requires PLT" flags in a
symbol. Symbol is a shared resource, and writing to them from multiple
threads without synchronization is unsafe. To deal with it, we made
the flag an atomic variable.
The other common pattern you can find in mold which is build on top of
the parallel for-loop is the map-reduce pattern. That is, we run a
parallel for-loop on a large data set to produce a small data set and
process the small data set with a single thread. Let me take a
build-id computation as an example. Build-id is typically computed by
applying a cryptographic hash function such as SHA-1 on a linker's
output file. To compute it, we first consider an output as a sequence
of 1 MiB blocks and compute a SHA-1 hash for each block in parallel.
Then, we concatenate the SHA-1 hashes and compute a SHA-1 hash on the
hashes to get a final build-id.
Finally, we use concurrent hashmap at a few places in mold. Concurrent
hashmap is a hashmap to which multiple threads can safely insert items
in parallel. We use it in the symbol resolution stage, for example.
To resolve symbols, we basically have to throw in all defined symbols
into a hash table, so that we can find a matching defined symbol for
an undefined symbol by name. We do the hash table insertion from a
parallel for-loop which iterates over a list of input files.
Overall, even though mold is highly scalable, it succeeded to avoid
complexties you often find in complex parallel programs. From high
level, mold just serially executes the linker's internal passes one by
one. Each pass is parallelized using parallel for-loops.
### Rejected ideas
In this section, I'll explain the alternative designs I currently do
not plan to implement and why I turned them down.
- Placing variable-length sections at end of an output file and start
copying file contents before fixing the output file layout
Idea: Fixing the layout of regular sections seems easy, and if we
place them at beginning of a file, we can start copying their
contents from their input files to an output file. While copying
file contents, we can compute the sizes of variable-length sections
such as .got or .plt and place them at end of the file.
Reason for rejection: I did not choose this design because I doubt
if it could actually shorten link time and I think I don't need it
anyway.
The linker has to de-duplicate comdat sections (i.e. inline
functions that are included in multiple object files), so we
cannot compute the layout of regular sections until we resolve all
symbols and de-duplicate comdats. That takes a few hundred
milliseconds. After that, we can compute the sizes of
variable-length sections in less than 100 milliseconds. It's quite
fast, so it doesn't seem to make much sense to proceed without
fixing the final file layout.
The other reason to reject this idea is because there's good a
chance for this idea to have a negative impact on linker's overall
performance. If we copy file contents before fixing the layout, we
can't apply relocations to them while copying because symbol
addresses are not available yet. If we fix the file layout first, we
can apply relocations while copying, which is effectively zero-cost
due to a very good data locality. On the other hand, if we apply
relocations long after we copy file contents, it's pretty expensive
because section contents are very likely to have been evicted from
CPU cache.
- Incremental linking
Idea: Incremental linking is a technique to patch a previous linker's
output file so that only functions or data that are updated from the
previous build are written to it. It is expected to significantly
reduce the amount of data copied from input files to an output file
and thus speed up linking. GNU BFD and gold linkers support it.
Reason for rejection: I turned it down because it (1) is
complicated, (2) doesn't seem to speed it up that much and (3) has
several practical issues. Let me explain each of them.
First, incremental linking for real C/C++ programs is not as easy as
one might think. Let me take malloc as an example. malloc is usually
defined by libc, but you can implement it in your program, and if
that's the case, the symbol `malloc` will be resolved to your
function instead of the one in libc. If you include a library that
defines malloc (such as libjemalloc or libtbbmallc) before libc,
their malloc will override libc's malloc.
Assume that you are using a nonstandard malloc. What if you remove
your malloc from your code, or remove `-ljemalloc` from your
Makefile? The linker has to include a malloc from libc, which may
include more object files to satisfy its dependencies. Such code
change can affect the entire program rather than just replacing one
function. The same is true for adding malloc to your program. Making
a local change doesn't necessarily result in a local change in the
binary level. It can easily have cascading effects.
Some ELF fancy features make incremental linking even harder to
implement. Take the weak symbol as an example. If you define `atoi`
as a weak symbol in your program, and if you are not using `atoi`
at all in your program, that symbol will be resolved to address
0. But if you start using some libc function that indirectly calls
`atoi`, then `atoi` will be included in your program, and your weak
symbol will be resolved to that function. I don't know how to
efficiently fix up a binary for this case.
This is a hard problem, so existing linkers don't try too hard to
solve it. For example, IIRC, gold falls back to full link if any
function is removed from a previous build. If you want to not annoy
users in the fallback case, you need to make full link fast anyway.
Second, incremental linking itself has an overhead. It has to detect
updated files, patch an existing output file and write additional
data to an output file for future incremental linking. GNU gold, for
instance, takes almost 30 seconds on my machine to do a null
incremental link (i.e. no object files are updated from a previous
build) for chrome. It's just too slow.
Third, there are other practical issues in incremental linking. It's
not reproducible, so your binary isn't going to be the same as other
binaries even if you are compiling the same source tree using the
same compiler toolchain. Or, it is complex and there might be a bug
in it. If something doesn't work correctly, "remove --incremental
from your Makefile and try again" could be a piece of advice, but
that isn't ideal.
So, all in all, incremental linking is tricky. I wanted to make full
link as fast as possible, so that we don't have to think about how
to work around the slowness of full link.
- Defining a completely new file format and use it
Idea: Sometimes, the ELF file format itself seems to be a limiting
factor in improving the linker's performance. We might be able to make a
far better one if we create a new file format.
Reason for rejection: I rejected the idea because it apparently has
a practical issue (backward compatibility issue) and also doesn't
seem to improve the performance of linkers that much. As clearly
demonstrated by mold, we can create a fast linker for ELF. I believe
ELF isn't that bad, after all. The semantics of the existing Unix
linkers, such as the name resolution algorithm or the linker script,
have slowed the linkers down, but that's not a problem of the file
format itself.
- Watching object files using inotify(2)
Idea: When mold is running as a daemon for preloading, use
inotify(2) to watch file system updates so that it can reload files
as soon as they are updated.
Reason for rejection: Just like the maximum number of files you can
simultaneously open, the maximum number of files you can watch using
inotify(2) isn't that large. Maybe just a single instance of mold is
fine with inotify(2), but it may fail if you run multiple of it.
The other reason for not doing it is because mold is quite fast
without it anyway. Invoking stat(2) on each file for file update
check takes less than 100 milliseconds for Chrome, and if most of
the input files are not updated, parsing updated files takes almost
no time.
-55
View File
@@ -1,55 +0,0 @@
This page explains the following warning message and how to fix it.
mold emits this message when it sees an object file that may not be
compatible with mold.
```
mold: warning: foo.o: this file may cause a segmentation fault because it requires an executable stack. See https://github.com/rui314/mold/tree/main/docs/execstack.md for more info.
```
# Background
On modern computers, the stack area (to which local variables are
stored) cannot contain executable code. If the control reaches the
stack area, the CPU refuses to execute any code there and the program
is usually terminated due to segmentation fault.
This is a security measure. The stack area used to be executable (old
CPUs generally execute any code as long as it is in a readable memory
region), but that provided an easy attack vector to a malicious user.
They wrote executable code to the stack area using some buffer
overflow bug and jumped there to run arbitrary code in a remote server
process.
To prevent this type of attack, the stack area is no longer executable
since the early 2000s. On Linux, the stack's executable-ness is
controlled by a bit in an executable, and the loader respects that
bit. The bit is set by the linker.
GCC had (and still has) a feature that depends on the executable
stack, so they invented a way to tell the linker to mark the stack
executable. Specifically, if an object file contains a
`.note.GNU-stack` section with the `SHF_EXECSTACK` bit, GNU linker
silently makes the stack of an output file executable.
But the GNU linker's behavior is dangerous. If you accidentally link
an object file that has that marker section, the entire stack area
silently becomes executable, disabling the security mechanism.
Therefore, mold simply ignores that marker section. If you are using
mold, you need to explicitly pass `-z execstack` to the linker to make
the stack executable.
# What caused this issue?
You are likely to use GCC's [Nested
Functions](https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html)
feature which still depends on the executable stack.
# How to fix it?
If you know what you are doing, pass `-z execstack` to mold. Beware
that this will significantly weaken your program's security.
If you don't want to pass `-z execstack`, rewrite your code so that
your code does not depend on the executable stack.
-158
View File
@@ -1,158 +0,0 @@
The very concept of linking is simple: a compiler compiles a piece of
source code into an object file (a file containing machine code), and
a linker combines object files into a single executable or a shared
library file. However, the actual implementation of the linker for
modern systems is much more complicated because hardware, operating
system, compiler and linker all have many more features.
In this file, I'll explain random topics in the glossary format that
you need to understand to read mold code.
## DSO
A .so file. Short for Dynamic Shared Object. Often called as a
shared library, a dynamic libray or a shared object as well.
An DSO contains common functions and data that are used by multiple
executables and/or other DSOs. At runtime, a DSO is loaded to a
contiguous region in the virtual address.
## Object file
A .o file. An object file contains machine code and data, but it
cannot be executed because it's not self-contained. For example,
if you compile a C source file containing a call of `printf`,
the actual function code of `printf` is not included in the resulting
object file. You include `stdio.h`, but that teaches the compiler
only about `printf`'s type, and the compiler still don't know what
`printf` actually does. Therefore, it cannot emit code for `printf`.
You need to link an object file with other object file or a shared
library to make it exectuable.
## Virtual address space
A pointer has a value like 0x803020 which is an address of the
pointee. But it doesn't mean that the pointee resides at the
physical memory address 0x803020 on the computer. Modern CPUs
contains so-called Mmeory Management Unit (MMU), and all access to
the memory are first translated by MMU to the physical address.
The address before translation is called the "virtual address".
Unless you are doing the kernel programming, all addresses you
handle are virtual addresses.
The OS kernel controls the MMU so that each process owns the entire
virtual address space. So, even if two process uses the same virtual
address, they don't conflict. They are mapped to different physical
addresses.
The existence of MMU has several implications to the linker. First,
we can link the main executable to a specific address. On process
startup, there's no code or data in the virtual address space, so
the mapping of the main executable always succeed. However, it's not
true to DSOs because they are loaded after the main executable and
possibly other DSOs. Therefore, shared libraries must be linked in a
way that they can be loaded to any address in the virtual address
space.
## Relocation
A piece of information for the linker as to how to link object files
or a dynamic objects.
Object files can refer functions or data in other object files. For
example, if you compile a function which calls a non-local function
`foo`, the resulting code contains something like this:
```
26: e8 00 00 00 00 callq 2b <bar+0xb>
27: R_X86_64_PLT32 foo-0x4
```
The above `callq` is the instruction to call a function at the
machine code level. It's opcode is `0xe8` in x86-64, so the
instruction begins with `0xe8`. The following four bytes are
displacement; that is, the address of the branch target relative to
the end of this `callq` instruction. Notice that the displacement is
0. The compiler couldn't fill the displacement because it has no
idea as to where `foo` will be at runtime. So, the compiler write 0
as a placeholder and instead write a relocation `R_X86_64_PLT32`
with `foo` as its associated symbol. The linker reads this
relocation, computes the offsets between this call instruction and
function `foo` and overwrite the placeholder value 0 with an actual
displacement.
There are many different types of relocations. For example, if you
want to fix up not with a displacement but with an absolute address
of a symbol, you need to use `R_X86_64_ABS64` instead.
## Static library
A .a file. Often called as an archive file or just archive as well.
A static library is a container just like tar or zip. Actually,
there's no technical reason to not use tar or (uncompressed) zip,
but traditionally the .a file format is used by the linker.
A static library contains object files and can be passed to the
linker along with other object files and/or archives.
A linker pulls out object files from an archive only if it is needed
to resolve undefined symbols. In other words, object files in an
archive are not linked by default and used as a complement to supply
missing definitions. This is ideal for a library because you don't
want to link library functions unless you are actually using them.
Contrary to archive files, object files directly given to a linker
are always linked to the output.
To maximize the benefit of archive files, a library often used as a
static library is broken down to small files to separate each
function individually (for example, look at
https://git.musl-libc.org/cgit/musl/tree/src/stdio). By doing this,
you import only used functions.
A static file is created by `ar`, whose command line arguments are
similar to `tar`. A static library contains the symbol table which
offers a quick way to look up an object file for a defined symbol,
but mold does not use the static library's symbol table. mold
doesdn't need a symbol table to exist in an archive, and if exists,
mold just ignores it.
See also: DSO (dynamic library)
## Symbol
A symbol is a label assigned to a specific location in an input file
or an output file. For example, if you define function `foo` and
compile it, the resulting object file contains a symbol `foo`
pointing to the beginning of the machine code for `foo`.
Usually, a symbol name is a function or a variable name. If an
object is anonymous (such the one for a string literal), a compiler
generated a unique symbol, which often starts with `.` to avoid
conflict with user-defined symbols.
For C++, symbol name is a complex "mangled" name. We need to mangle
identifiers because a simple name such as `foo` cannot be uniquely
identify a function or a data in C++, because for example `foo` may
be in a namespace or defined as a static member in some class. If
`foo` is an overloaded function, we need to distinguish different
`foo`s by its type. Therefore, C++ compiler mangles an identifier by
appending nmaepsace names, type information and such so that
different things get different names.
For example, a function `int foo(int)` in a namespace `bar` is
mangled as `_ZN3bar3fooEi`.
A symbol can be either defined or undefined. A defined symbol points
to some location in a file which may contain the function's machine
code or the variable's initial value. An undefined symbol does not
point to anywhere. It needs to be merged with a defined symbol with
the same name at link-time. This merging process is called "name
resolution".
For example, if your program is using `printf`, it usually contains
`printf` as an undefined symbol. You need to link it with `libc.a`
or `libc.so`, which contain a defined symbol of `printf`, to make a
complete program.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

+121 -38
View File
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "MOLD" "1" "November 2023" ""
.TH "MOLD" "1" "October 2026" ""
.SH "NAME"
\fBmold\fR \- a modern linker
.SH "SYNOPSIS"
@@ -10,9 +10,9 @@
.SS "How to use"
See \fIhttps://github\.com/rui314/mold#how\-to\-use\fR\.
.SS "Compatibility"
\fBMold\fR is designed to be a drop\-in replacement for the GNU linkers for linking user\-land programs\. If your user\-land program cannot be built due to missing command\-line options, please file a bug at \fIhttps://github\.com/rui314/mold/issues\fR\.
\fBmold\fR is designed to be a drop\-in replacement for the GNU linkers for linking user\-land programs\. If your user\-land program cannot be built due to missing command\-line options, please file a bug at \fIhttps://github\.com/rui314/mold/issues\fR\.
.P
Mold supports a very limited set of linker script features, which is just sufficient to read \fB/usr/lib/x86_64\-linux\-gnu/libc\.so\fR on Linux systems (on Linux, that file is contrary to its name not a shared library but an ASCII linker script that loads a real \fBlibc\.so\fR file\.)
\fBmold\fR supports a very limited set of linker script features, which is just sufficient to read \fB/usr/lib/x86_64\-linux\-gnu/libc\.so\fR on Linux systems (on Linux, that file is contrary to its name not a shared library but an ASCII linker script that loads a real \fBlibc\.so\fR file\.)
.P
Beyond that, we have no plan to support any additional linker script features\. The linker script is an ad\-hoc, over\-designed, complex language which we believe needs to be replaced by a simpler mechanism\. We have a plan to add a replacement for the linker script to \fBmold\fR instead\.
.SS "Archive symbol resolution"
@@ -40,15 +40,19 @@ Typically, an ELF module that exports a symbol also imports the same symbol\. Su
.P
Let's take \fBmalloc\fR as an example\. Assume that you define your version of \fBmalloc\fR in your main executable file\. Then, all \fBmalloc\fR calls from any module are resolved to your function instead of the one in libc, because the executable is always at the beginning of the dynamic symbol search list\. Note that even \fBmalloc\fR calls within libc are resolved to your definition since libc exports and imports \fBmalloc\fR\. Therefore, by defining \fBmalloc\fR yourself, you can overwrite a library function, and the \fBmalloc\fR in libc becomes dead code\.
.P
These Unix semantics are tricky and sometimes considered harmful\. For example, assume that you accidentally define \fBatoi\fR as a global function in your executable that behaves completely differently from the one in the C standard\. Then, all \fBatoi\fR function calls from any modules (even function calls within libc) are redirected to your function instead of the one in libc, which will very likely cause a problem\.
.P
That is a somewhat surprising consequence for an accidental name conflict\. On the other hand, this semantic is sometimes useful because it allows users to override library functions without rebuilding modules containing them\.
These Unix semantics are tricky and sometimes considered harmful\. For example, assume that you accidentally define \fBatoi\fR as a global function in your executable that behaves completely differently from the one in the C standard\. Then, all \fBatoi\fR function calls from any modules (even function calls within libc) are redirected to your function instead of the one in libc, which will very likely cause a problem\. That is a somewhat surprising consequence for an accidental name conflict\. On the other hand, this semantic is sometimes useful because it allows users to override library functions without rebuilding modules containing them\.
.P
Whether good or bad, you should keep these semantics in mind to understand Unix linkers' behaviors\.
.SS "Build reproducibility"
\fBmold\fR's output is deterministic\. That is, if you pass the same object files and the same command\-line options to the same version of \fBmold\fR, it is guaranteed that \fBmold\fR produces the bit\-by\-bit identical output\. The linker's internal randomness, such as the timing of thread scheduling or iteration orders of hash tables, doesn't affect the output\.
\fBmold\fR's output is deterministic\. That is, if you pass the same object files and the same command\-line options to the same version of \fBmold\fR, it is guaranteed that \fBmold\fR produces the bit\-for\-bit identical output\. The linker's internal randomness, such as the timing of thread scheduling or iteration orders of hash tables, doesn't affect the output\.
.P
\fBmold\fR does not have any host\-specific default settings\. This is contrary to the GNU linkers, for which some configurable values, such as system\-dependent library search paths, are hard\-coded\. \fBmold\fR depends only on its command\-line arguments\.
.SH "OPTION NOTATIONS"
Long options are spelled with two dashes\. Some also accept a single dash, but which ones do is somewhat arbitrary for historical reasons\. \fBmold\fR follows the conventions of other linkers for compatibility\. For example, \fB\-\-export\-dynamic\fR can be spelled \fB\-export\-dynamic\fR, while \fB\-\-execute\-only\fR requires two dashes\.
.P
This can cause unexpected behavior when constructing command\-line options in a script\. For example, you might use \fB"\-e$foo"\fR to set the entry point symbol to the value of \fB$foo\fR\. However, if \fB$foo\fR is \fBxport\-dynamic\fR, the argument becomes \fB\-export\-dynamic\fR and is interpreted as \fB\-\-export\-dynamic\fR\.
.P
To avoid such ambiguities, we recommend always using two dashes for long options and separating a single\-letter option from its argument with a space\. For example, use \fB\-e $foo\fR or \fB\-\-entry=$foo\fR to specify the entry point symbol in a script\.
.SH "MOLD\-SPECIFIC OPTIONS"
.TP
\fB\-\-chroot\fR=\fIdir\fR
@@ -63,8 +67,13 @@ Synonym for \fB\-\-color\-diagnostics=auto\fR\.
\fB\-\-no\-color\-diagnostics\fR
Synonym for \fB\-\-color\-diagnostics=never\fR\.
.TP
\fB\-\-detach\fR, \fB\-\-no\-detach\fR
Permit or do not permit mold to create a debug info file in the background\.
.TP
\fB\-\-fork\fR, \fB\-\-no\-fork\fR
Spawn a child process and let it do the actual linking\. When linking a large program, the OS kernel can take a few hundred milliseconds to terminate a \fBmold\fR process\. \fB\-\-fork\fR hides that latency\. By default, it does fork\.
.IP
Note that \fB\-\-fork\fR also hides the resource usage statistics reported by time(2), since it doesn't call waitpid(2) on the child process\. If you need those statistics, pass \fB\-\-no\-fork\fR\.
.TP
\fB\-\-perf\fR
Print performance statistics\.
@@ -74,6 +83,11 @@ Print out dependency information for input files\.
.IP
Each line of the output for this option shows which file depends on which file to use a specific symbol\. This option is useful for debugging why some object file in a static archive got linked or why some shared library is kept in an output file's dependency list even with \fB\-\-as\-needed\fR\.
.TP
\fB\-\-relocatable\-merge\-sections\fR
By default, \fBmold\fR doesn't merge input sections by name when merging input object files into a single output object file for \fB\-r\fR\. For example, \fB\.text\.foo\fR and \fB\.text\.bar\fR aren't merged for \fB\-r\fR even though they are merged into \fB\.text\fR based on the default section merging rules\.
.IP
This option changes the behavior so that \fBmold\fR merges input sections by name by the default section merging rules\.
.TP
\fB\-\-repro\fR
Archive input files, as well as a text file containing command line options, in a tar file so that you can run \fBmold\fR with the exact same inputs again\. This is useful for reporting a bug with a reproducer\. The output filename is \fBpath/to/output\.tar\fR, where \fBpath/to/output\fR is an output filename specified by \fB\-o\fR\.
.TP
@@ -85,7 +99,16 @@ This option is useful for finding bugs that depend on the initialization order o
By reversing the order of input sections using \fB\-\-reverse\-sections\fR, you can easily test that your program works in the reversed initialization order\.
.TP
\fB\-\-run\fR \fIcommand\fR \fIarg\fR\|\.\|\.\|\.
Run \fIcommand\fR with \fBmold\fR \fB/usr/bin/ld\fR\. Specifically, \fBmold\fR runs a given command with the \fBLD_PRELOAD\fR environment set to intercept exec(3) family functions and replaces \fBargv[0]\fR with itself if it is \fBld\fR, \fBld\.gold\fR, or \fBld\.lld\fR\.
Run \fIcommand\fR with \fBmold\fR as \fB/usr/bin/ld\fR\. Specifically, \fBmold\fR runs a given command with the \fBLD_PRELOAD\fR environment set to intercept exec(3) family functions and replaces \fBargv[0]\fR with itself if it is \fBld\fR, \fBld\.gold\fR, or \fBld\.lld\fR\.
.TP
\fB\-\-separate\-debug\-file\fR, \fB\-\-separate\-debug\-file\fR=\fIfile\fR
Bundle debug info sections into a separate file instead of embedding them in an output executable or a shared library\. mold creates a debug info file in the background by default, so that you can start running your executable as soon as possible\.
.IP
By default, the debug info file is created in the same directory as is the output file, with the \fB\.dbg\fR file extension\. That filename is embedded into the output file so that \fBgdb\fR can automatically find the debug info file for the output file\. For more info about gdb features related to separate debug files, see \fIhttps://sourceware\.org/gdb/current/onlinedocs/gdb\.html/Separate\-Debug\-Files\.html\fR\.
.IP
mold holds a file lock with flock(2) while creating a debug info file in the background\.
.IP
If you don't want to create a debug info file in the background, pass the \fB\-\-no\-detach\fR option\.
.TP
\fB\-\-shuffle\-sections\fR, \fB\-\-shuffle\-sections\fR=\fInumber\fR
Randomize the output by shuffling the order of input sections before assigning them the offsets in the output file\. If a \fInumber\fR is given, it's used as a seed for the random number generator, so that the linker produces the same output for the same seed\. If no seed is given, a random number is used as a seed\.
@@ -94,6 +117,11 @@ This option is useful for benchmarking\. Modern CPUs are sensitive to a program'
.IP
By running a benchmark multiple times with randomized memory layouts using \fB\-\-shuffle\-sections\fR, you can isolate your program's real performance number from the randomness caused by memory layout changes\.
.TP
\fB\-\-spare\-program\-headers\fR=\fInumber\fR
Append the given number of \fBPT_NULL\fR entries to the end of the program header, so that post\-link processing tools can easily add new segments by overwriting the null entries\.
.IP
Note that ELF requires all \fBPT_LOAD\fR segments to be sorted by \fBp_vaddr\fR\. Therefore, if you add a new LOAD segment, you may need to sort the entire program header\.
.TP
\fB\-\-stats\fR
Print input statistics\.
.TP
@@ -105,6 +133,30 @@ Use multiple threads\. By default, \fBmold\fR uses as many threads as the number
.TP
\fB\-\-quick\-exit\fR, \fB\-\-no\-quick\-exit\fR
Use or do not use \fBquick_exit\fR to exit\.
.TP
\fB\-\-zero\-to\-bss\fR
Convert all\-zero data sections into BSS\.
.IP
When this option is enabled, \fBmold\fR scans input data sections that are not of type \fBSHT_NOBITS\fR and checks whether their contents consist solely of zero bytes\. Such sections are then converted into BSS (\fBSHT_NOBITS\fR) sections\. This reduces the size of the output file, since BSS sections occupy no space in the file image\.
.IP
This behavior is especially useful for user\-defined sections created with \fB__attribute__((section("\.sectname")))\fR that contain uninitialized global variables\. GCC and Clang do not automatically mark such sections as BSS even if their contents are entirely zero, and instead emit them as regular data sections\.
.IP
For example, consider \fB__attribute__((section("\.sectname"))) int vec[256];\fR\.
.IP
By default, this results in a \fB\.sectname\fR section of type \fBSHT_PROGBITS\fR filled with zeros\. With \fB\-\-zero\-to\-bss\fR, the linker will recognize it as empty data and convert it to a \fBSHT_NOBITS\fR section, reducing the output file size without changing runtime semantics\.
.TP
\fB\-z rewrite\-endbr\fR, \fB\-z norewrite\-endbr\fR
As a security measure, some CPU instruction sets have recently gained a feature to protect control flow integrity by disallowing indirect branches by default\. If the feature is enabled, the instruction that is executed immediately after an indirect branch must be an branch target marker instruction, or a CPU\-level fault will raise\. The marker instruction is also known as "landing pad" instruction, to which indirect branches can land\. This feature makes ROP attacks harder to conduct\.
.IP
To use the feature, a function whose pointer is taken needs to begin with a landing pad because a function call via a function pointer is compiled to an indirect branch\. On the other hand, if a function is called only directly (i\.e\. referred to only by \fIdirect\fR branch instructions), it doesn't have to begin with it\.
.IP
By default, the compiler always emits a landing pad at the beginning of each global function because it doesn't know whether or not the function's pointer is taken in another translation unit\. As a result, the resulting binary has more attack surface than necessary\.
.IP
If \fB\-\-rewrite\-endbr\fR is given, mold conducts a whole program analysis to identify functions whose addresses are actually taken and rewrites landing pads with no\-ops for non\-address\-taken functions, reducing the attack surface\.
.IP
This feature is currently available only on x86\-64 and arm64\.
.IP
On arm64, the landing pad is \fBbti c\fR\. Note that code compiled with \fB\-mbranch\-protection=standard\fR (BTI + pointer authentication) starts functions with \fBpaciasp\fR, which doubles as a landing pad, so there is no separate \fBbti c\fR to rewrite and this option has no effect on such code\.
.SH "GNU\-COMPATIBLE OPTIONS"
.TP
\fB\-\-help\fR
@@ -145,7 +197,10 @@ Omit \fB\.debug_*\fR sections from the output file\.
Read linker script from \fIfile\fR\.
.TP
\fB\-X\fR, \fB\-\-discard\-locals\fR
Discard temporary local symbols to reduce the sizes of the symbol table and the string table\. Temporary local symbols are local symbols starting with \fB\.L\fR\. Compilers usually generate such symbols for unnamed program elements such as string literals or floating\-point literals\.
Discard temporary local symbols to reduce the sizes of the symbol table and the string table\. Temporary local symbols are local symbols starting with \fB\.L\fR\. Compilers usually generate such symbols for unnamed program elements such as string literals or floating\-point literals\. This is the default; use \fB\-\-discard\-none\fR to keep temporary local symbols\.
.TP
\fB\-\-discard\-none\fR
Keep all local symbols in the symbol table, including temporary ones starting with \fB\.L\fR, except ones in mergeable sections\. This restores the default behavior of GNU ld\. \fBmold\fR and \fBld\.lld\fR discard temporary local symbols by default\.
.TP
\fB\-e\fR \fIsymbol\fR, \fB\-\-entry\fR=\fIsymbol\fR:
@@ -168,11 +223,6 @@ Use \fIfile\fR as the output file name instead of the default name \fBa\.out\fR\
\fB\-r\fR, \fB\-\-relocatable\fR
Instead of generating an executable or a shared object file, combine input object files to generate another object file that can be used as an input to a linker\.
.TP
\fB\-\-relocatable\-merge\-sections\fR
By default, \fBmold\fR doesn't merge input sections by name when merging input object files into a single output object file for \fB\-r\fR\. For example, \fB\.text\.foo\fR and \fB\.text\.bar\fR aren't merged for \fB\-r\fR even though they are merged into \fB\.text\fR according to the default section merging rules\.
.IP
This option changes the behavior so that \fBmold\fR merges input sections by name by the default section merging rules\.
.TP
\fB\-s\fR, \fB\-\-strip\-all\fR
Omit \fB\.symtab\fR section from the output file\.
.TP
@@ -194,8 +244,14 @@ When creating a shared library, make global symbols export\-only (i\.e\. do not
\fB\-\-Bsymbolic\-functions\fR
This option has the same effect as \fB\-\-Bsymbolic\fR but works only for function symbols\. Data symbols remain being both imported and exported\.
.TP
\fB\-\-Bsymbolic\-non\-weak\fR
This option has the same effect as \fB\-\-Bsymbolic\fR but works only for non\-weak symbols\. Weak symbols remain being both imported and exported\.
.TP
\fB\-\-Bsymbolic\-non\-weak\-functions\fR
This option has the same effect as \fB\-\-Bsymbolic\fR but works only for non\-weak function symbols\. Data symbols and weak function symbols remain being both imported and exported\.
.TP
\fB\-\-Bno\-symbolic\fR
Cancel \fB\-\-Bsymbolic\fR and \fB\-\-Bsymbolic\-functions\fR\.
Cancel \fB\-\-Bsymbolic\fR, \fB\-\-Bsymbolic\-functions\fR, \fB\-\-Bsymbolic\-non\-weak\fR and \fB\-\-Bsymbolic\-non\-weak\-functions\fR\.
.TP
\fB\-\-Map\fR=\fIfile\fR
Write map file to \fIfile\fR\.
@@ -209,16 +265,26 @@ Alias for \fB\-\-section\-start=\.data=\fR\fIaddress\fR\.
\fB\-\-Ttext\fR=\fIaddress\fR
Alias for \fB\-\-section\-start=\.text=\fR\fIaddress\fR\.
.TP
\fB\-\-Ttext\-segment\fR=\fIaddress\fR
Set the address of the first byte of the text segment\.
.TP
\fB\-\-allow\-multiple\-definition\fR
Normally, the linker reports an error if there are more than one definition of a symbol\. This option changes the default behavior so that it doesn't report an error for duplicate definitions and instead use the first definition\.
.TP
\fB\-\-allow\-shlib\-undefined\fR, \fB\-\-no\-allow\-shlib\-undefined\fR
Even if mold succeeds in linking a main executable without undefined symbol errors, you may still encounter symbol lookup errors at runtime because the dynamic linker cannot find some symbols in shared libraries in any ELF module\. This occurs because mold ignores undefined symbols in shared libraries by default\.
.IP
If you pass \fB\-\-no\-allow\-shlib\-undefined\fR, mold verifies that undefined symbols in shared libraries given to the linker can be resolved at link\-time\. In other words, this converts the runtime error to a link\-time error\.
.IP
Note that you need to pass all shared libraries, including indirectly dependent ones, to the linker as arguments for \fB\-l\fR\. If a shared library depends on a library that's not passed to the linker, the verification will be skipped for that file\.
.TP
\fB\-\-as\-needed\fR, \fB\-\-no\-as\-needed\fR
By default, shared libraries given to the linker are unconditionally added to the list of required libraries in an output file\. However, shared libraries after \fB\-\-as\-needed\fR are added to the list only when at least one symbol is actually used by the output file\. In other words, shared libraries after \fB\-\-as\-needed\fR are not added to the list of needed libraries if they are not needed by a program\.
.IP
The \fB\-\-no\-as\-needed\fR option restores the default behavior for subsequent files\.
.TP
\fB\-\-build\-id\fR=[ \fBmd5\fR | \fBsha1\fR | \fBsha256\fR | \fBuuid\fR | \fB0x\fR\fIhexstring\fR | \fBnone\fR ]
Create a \fB\.note\.gnu\.build\-id\fR section containing a byte string to uniquely identify an output file\. \fBsha256\fR compute a 256\-bit cryptographic hash of an output file and set it to build\-id\. \fBmd5\fR and \fBsha1\fR compute the same hash but truncate it to 128 and 160 bits, respectively, before setting it to build\-id\. \fBuuid\fR sets a random 128\-bit UUID\. \fB0x\fR\fIhexstring\fR sets \fIhexstring\fR\.
\fB\-\-build\-id\fR=[ \fBmd5\fR | \fBsha1\fR | \fBsha256\fR | \fBfast\fR | \fBuuid\fR | \fB0x\fR\fIhexstring\fR | \fBnone\fR ]
Create a \fB\.note\.gnu\.build\-id\fR section containing a byte string to uniquely identify an output file\. \fBsha256\fR compute a 256\-bit cryptographic hash of an output file and set it to build\-id\. \fBmd5\fR and \fBsha1\fR compute the same hash but truncate it to 128 and 160 bits, respectively, before setting it to build\-id\. \fBuuid\fR sets a random 128\-bit UUID\. \fB0x\fR\fIhexstring\fR sets \fIhexstring\fR\. \fBfast\fR is a synonym for \fBsha256\fR\.
.TP
\fB\-\-build\-id\fR
Synonym for \fB\-\-build\-id=sha256\fR\.
@@ -226,8 +292,8 @@ Synonym for \fB\-\-build\-id=sha256\fR\.
\fB\-\-no\-build\-id\fR
Synonym for \fB\-\-build\-id=none\fR\.
.TP
\fB\-\-compress\-debug\-sections\fR=[ \fBzlib\fR | \fBzlib\-gabi\fR | \fBzstd\fR | \fBnone\fR ]
Compress DWARF debug info (\fB\.debug_*\fR sections) using the zlib or zstd compression algorithm\. \fBzlib\-gabi\fR is an alias for \fBzlib\fR\.
\fB\-\-compress\-debug\-sections\fR=[ \fBzlib\fR | \fBzlib\-gabi\fR | \fBzlib:\fR\fIN\fR | \fBzstd\fR | \fBzstd:\fR\fIN\fR | \fBnone\fR ]
Compress DWARF debug info (\fB\.debug_*\fR sections) using the zlib or zstd compression algorithm\. \fBzlib\-gabi\fR is an alias for \fBzlib\fR\. \fBzlib:\fR\fIN\fR selects the zlib compression level, where \fIN\fR is between 0 and 9 (0 means no compression), and \fBzstd:\fR\fIN\fR selects the zstd compression level, where \fIN\fR is between 1 and 22\. Higher levels achieve better compression ratios but are slower\. \fBzlib\fR is equivalent to \fBzlib:1\fR and \fBzstd\fR is equivalent to \fBzstd:3\fR\.
.TP
\fB\-\-defsym\fR=\fIsymbol\fR=\fIvalue\fR
Define \fIsymbol\fR as an alias for \fIvalue\fR\.
@@ -257,10 +323,8 @@ The \fB\-\-emit\-relocs\fR instructs the linker to leave relocation sections in
\fB\-\-enable\-new\-dtags\fR, \fB\-\-disable\-new\-dtags\fR
By default, \fBmold\fR emits \fBDT_RUNPATH\fR for \fB\-\-rpath\fR\. If you pass \fB\-\-disable\-new\-dtags\fR, \fBmold\fR emits \fBDT_RPATH\fR for \fB\-\-rpath\fR instead\.
.TP
\fB\-\-execute\-only\fR
Traditionally, most processors require both executable and readable bits to 1 to make the page executable, which allows machine code to be read as data at runtime\. This is actually what an attacker often does after gaining a limited control of a process to find pieces of machine code they can use to gain the full control of the process\. As a mitigation, some recent processors allows "execute\-only" pages\. If a page is execute\-only, you can call a function there as long as you know its address but can't read it as data\.
.IP
This option marks text segments execute\-only\. This option currently works only on some ARM64 processors\.
\fB\-\-execute\-only\fR:
.TP
\fB\-\-exclude\-libs\fR=\fIlibraries\fR \|\.\|\.\|\.
Mark all symbols in the given \fIlibraries\fR hidden\.
@@ -312,13 +376,21 @@ Report undefined symbols (even with \fB\-\-shared\fR)\.
\fB\-\-noinhibit\-exec\fR
Create an output file even if errors occur\.
.TP
\fB\-\-pack\-dyn\-relocs\fR=[ \fBrelr\fR | \fBnone\fR ]
\fB\-\-package\-metadata\fR=\fIpercent\-encoded\-string\fR
Embed a specified string into the \fB\.note\.package\fR section\. This option is designed for build scripts that generate binary packages, such as \fB\.rpm\fR or \fB\.deb\fR, to include package metadata in each executable\. It simplifies the process of identifying the corresponding package for a given executable or core file\.
.IP
An argument to this option is treated as percent\-encoded and decoded before being inserted into the section, allowing you to avoid the use of the comma (\fB,\fR) character in the argument\. This is useful because the compiler replaces all occurrences of commas in \fB\-Wl,\fR with spaces before forwarding them to the linker\. Note that \fBmold\fR always interprets the argument as percent\-encoded, so you also need to escape all occurrences of \fB%\fR as \fB%25\fR\.
.TP
\fB\-\-pack\-dyn\-relocs\fR=[ \fBrelr\fR | \fBandroid\fR | \fBandroid+relr\fR | \fBnone\fR ]
Compress dynamic relocations in the output file\.
.IP
If \fBrelr\fR is specified, all \fBR_*_RELATIVE\fR relocations are put into \fB\.relr\.dyn\fR section instead of \fB\.rel\.dyn\fR or \fB\.rela\.dyn\fR section\. Since \fB\.relr\.dyn\fR section uses a space\-efficient encoding scheme, specifying this flag can reduce the size of the output\. This is typically most effective for position\-independent executable\.
.IP
Note that a runtime loader has to support \fB\.relr\.dyn\fR to run executables or shared libraries linked with \fB\-\-pack\-dyn\-relocs=relr\fR\. As of 2022, only ChromeOS, Android and Fuchsia support it\.
.TP
\fB\-\-package\-metadata\fR=\fIstring\fR
Embed \fIstring\fR to a \fB\.note\.package\fR section\. This option is intended to be used by a package management command such as rpm(8) to embed metadata regarding a package to each executable file\.
If \fBandroid\fR is specified, all dynamic relocations are encoded in Android's packed relocation format (APS2) and stored in the \fB\.rel\.dyn\fR or \fB\.rela\.dyn\fR section, whose section type is changed to \fBSHT_ANDROID_REL\fR or \fBSHT_ANDROID_RELA\fR\. The dynamic table is updated to use \fBDT_ANDROID_REL\fR / \fBDT_ANDROID_RELA\fR and their \fBSZ\fR variants in place of the standard tags\.
.IP
\fBandroid+relr\fR combines both: \fBR_*_RELATIVE\fR relocations go to \fB\.relr\.dyn\fR and the rest are APS2\-encoded\. This is the configuration used by most Android system binaries\.
.IP
Note that a runtime loader has to support \fB\.relr\.dyn\fR to run executables or shared libraries linked with \fB\-\-pack\-dyn\-relocs=relr\fR\. As of 2026, glibc (since 2\.36), musl (since 1\.2\.4), Android, ChromeOS, and Fuchsia all support it\. The APS2 format, on the other hand, is only handled by Android's bionic loader; the encoder itself is target\-agnostic, but the produced binary can only be loaded on platforms where bionic (or a compatible loader) handles \fBDT_ANDROID_REL(A)\fR\.
.TP
\fB\-\-pie\fR, \fB\-\-pic\-executable\fR, \fB\-\-no\-pie\fR, \fB\-\-no\-pic\-executable\fR
Create a position\-independent executable\.
@@ -355,10 +427,7 @@ Set \fIaddress\fR to section\. \fIaddress\fR is a hexadecimal number that may st
Create a share library\.
.TP
\fB\-\-spare\-dynamic\-tags\fR=\fInumber\fR
Reserve the given \fInumber\fR of tags in \fB\.dynamic\fR section\.
.TP
\fB\-\-spare\-program\-headers\fR=\fInumber\fR
Reserve the given \fInumber\fR of entries in the program header\.
Append the given number of \fBDT_NULL\fR entries to the end of the \fB\.dynamic\fR section, so that post\-link processing tools can easily add new dynamic tags by overwriting the null entries\.
.TP
\fB\-\-start\-lib\fR, \fB\-\-end\-lib\fR
Handle object files between \fB\-\-start\-lib\fR and \fB\-\-end\-lib\fR as if they were in an archive file\. That means object files between them are linked only when they are needed to resolve undefined symbols\. The options are useful if you want to link object files only when they are needed but want to avoid the overhead of running ar(3)\.
@@ -372,6 +441,9 @@ Set target system root directory to \fIdir\fR\.
\fB\-\-trace\fR
Print name of each input file\.
.TP
\fB\-\-undefined\-glob\fR=\fIpattern\fR
Synonym for \fB\-\-undefined\fR, except that \fB\-\-undefined\-glob\fR takes a glob pattern instead of just a single symbol name\.
.TP
\fB\-\-undefined\-version\fR, \fB\-\-no\-undefined\-version\fR
By default, \fBmold\fR warns on a symbol specified by a version script or by \fB\-\-export\-dynamic\-symbol\fR if it is not defined\. You can silence the warning by \fB\-\-undefined\-version\fR\.
.TP
@@ -384,6 +456,9 @@ How to handle undefined symbols\.
\fB\-\-version\-script\fR=\fIfile\fR
Read version script from \fIfile\fR\. If \fIfile\fR does not exist in the current directory, it is searched from library search paths for the sake of compatibility with GNU ld\.
.TP
\fB\-w\fR, \fB\-\-no\-warnings\fR
Suppress warnings and cancel \fB\-\-fatal\-warnings\fR\.
.TP
\fB\-\-warn\-common\fR, \fB\-\-no\-warn\-common\fR
Warn about common symbols\.
.TP
@@ -424,8 +499,11 @@ By default, the pages for the stack area (i\.e\. the pages where local variables
\fB\-z keep\-text\-section\-prefix\fR, \fB\-z nokeep\-text\-section\-prefix\fR
Keep \fB\.text\.hot\fR, \fB\.text\.unknown\fR, \fB\.text\.unlikely\fR, \fB\.text\.startup\fR, and \fB\.text\.exit\fR as separate sections in the final binary instead of merging them as \fB\.text\fR\.
.TP
\fB\-z rodynamic\fR
Make the \fB\.dynamic\fR section read\-only\.
.TP
\fB\-z relro\fR, \fB\-z norelro\fR
Some sections such as \fB\.dynamic\fR have to be writable only during an executable or a shared library file is being loaded to memory\. Once the dynamic linker finishes its job, such sections won't be mutated by anyone\. As a security mitigation, it is preferred to make such segments read\-only during program execution\.
Some sections such as \fB\.dynamic\fR have to be writable only during a module is being loaded to memory\. Once the dynamic linker finishes its job, such sections won't be mutated by anyone\. As a security mitigation, it is preferred to make such segments read\-only during program execution\.
.IP
\fB\-z relro\fR puts such sections into a special segment called \fBrelro\fR\. The dynamic linker makes a relro segment read\-only after it finishes its job\.
.IP
@@ -437,23 +515,28 @@ By default, \fBmold\fR generates a relro segment\. \fB\-z norelro\fR disables th
\fB\-z separate\-loadable\-segments\fR, \fB\-z separate\-code\fR, \fB\-z noseparate\-code\fR
If one memory page contains multiple segments, the page protection bits are set in such a way that the needed attributes (writable or executable) are satisfied for all segments\. This usually happens at a boundary of two segments with two different attributes\.
.IP
\fBseparate\-loadable\-segments\fR adds paddings between segments with different attributes so that they do not share the same page\. This is the default\.
\fBseparate\-loadable\-segments\fR adds paddings between segments with different attributes so that they do not share the same page\.
.IP
\fBseparate\-code\fR adds paddings only between executable and non\-executable segments\.
.IP
\fBnoseparate\-code\fR does not add any paddings between segments\.
\fBnoseparate\-code\fR does not add any paddings between segments\. This is the default\.
.TP
\fB\-z defs\fR, \fB\-z nodefs\fR
\fB\-z defs\fR, \fB\-z undefs\fR
Report undefined symbols (even with \fB\-\-shared\fR)\.
.TP
\fB\-z shstk\fR
Enforce shadow stack by turning \fBGNU_PROPERTY_X86_FEATURE_1_SHSTK\fR bit in \fB\.note\.gnu\.property\fR output section\. Shadow stack is part of Intel Control\-flow Enforcement Technology (CET), which is available since Tiger Lake (2020)\.
.TP
\fB\-z start\-stop\-visibility\fR=[ \fBhidden\fR | \fBprotected\fR ]
If a section name is valid as a C identifier (i\.e\., it matches \fB/^[_a\-zA\-Z][_a\-zA\-Z0\-9]*$/\fR), mold creates \fB__start_SECNAME\fR and \fB__stop_SECNAME\fR symbols to mark the beginning and end of the section, where \fBSECNAME\fR is the section name\.
.IP
You can make these marker symbols visible from other ELF modules by passing \fB\-z start\-stop\-visibility=protected\fR\. Default is \fBhidden\fR\.
.TP
\fB\-z text\fR, \fB\-z notext\fR, \fB\-z textoff\fR
\fBmold\fR by default reports an error if dynamic relocations are created in read\-only sections\. If \fB\-z notext\fR or \fB\-z textoff\fR are given, \fBmold\fR creates such dynamic relocations without reporting an error\. \fB\-z text\fR restores the default behavior\.
.TP
\fB\-z max\-page\-size\fR=\fInumber\fR
Some CPU ISAs support multiple different memory page sizes\. This option specifies the maximum page size that an output binary can run on\. The default value is 4 KiB for i386, x86\-64, and RISC\-V, and 64 KiB for ARM64\.
Some CPU ISAs support multiple memory page sizes\. This option specifies the maximum page size that an output binary can run on\. In general, binaries built for a larger page size can run on a system with a smaller page size, but not vice versa\. The default value is 4 KiB for i386, x86\-64, and RISC\-V, and 64 KiB for ARM64\.
.TP
\fB\-z nodefaultlib\fR
Make the dynamic loader ignore default search paths\.
@@ -476,7 +559,7 @@ Mark DSO to be initialized first at runtime\.
\fB\-z interpose\fR
Mark object to interpose all DSOs but executable\.
.TP
\fB\-(\fR, \fB\-)\fR, \fB\-EL\fR, \fB\-O\fR\fInumber\fR, \fB\-\-allow\-shlib\-undefined\fR, \fB\-\-dc\fR, \fB\-\-dp\fR, \fB\-\-end\-group\fR, \fB\-\-no\-add\-needed\fR, \fB\-\-no\-allow\-shlib\-undefined\fR, \fB\-\-no\-copy\-dt\-needed\-entries\fR, \fB\-\-no\-fatal\-warnings\fR, \fB\-\-nostdlib\fR, \fB\-\-rpath\-link=Ar dir\fR, \fB\-\-sort\-common\fR, \fB\-\-sort\-section\fR, \fB\-\-start\-group\fR, \fB\-\-warn\-constructors\fR, \fB\-\-warn\-once\fR, \fB\-\-fix\-cortex\-a53\-835769\fR, \fB\-\-fix\-cortex\-a53\-843419\fR, \fB\-z combreloc\fR, \fB\-z common\-page\-size\fR, \fB\-z nocombreloc\fR
\fB\-(\fR, \fB\-)\fR, \fB\-EL\fR, \fB\-O\fR\fInumber\fR, \fB\-\-dc\fR, \fB\-\-dp\fR, \fB\-\-end\-group\fR, \fB\-\-no\-add\-needed\fR, \fB\-\-no\-copy\-dt\-needed\-entries\fR, \fB\-\-nostdlib\fR, \fB\-\-rpath\-link=Ar dir\fR, \fB\-\-sort\-common\fR, \fB\-\-sort\-section\fR, \fB\-\-start\-group\fR, \fB\-\-warn\-constructors\fR, \fB\-\-warn\-once\fR, \fB\-\-fix\-cortex\-a53\-835769\fR, \fB\-\-fix\-cortex\-a53\-843419\fR, \fB\-z combreloc\fR, \fB\-z common\-page\-size\fR, \fB\-z nocombreloc\fR
Ignored
.SH "ENVIRONMENT VARIABLES"
.TP
+260 -61
View File
@@ -13,12 +13,12 @@ See <https://github.com/rui314/mold#how-to-use>.
### Compatibility
**Mold** is designed to be a drop-in replacement for the GNU linkers for
`mold` is designed to be a drop-in replacement for the GNU linkers for
linking user-land programs. If your user-land program cannot be built due to
missing command-line options, please file a bug at
<https://github.com/rui314/mold/issues>.
Mold supports a very limited set of linker script features, which is just
`mold` supports a very limited set of linker script features, which is just
sufficient to read `/usr/lib/x86_64-linux-gnu/libc.so` on Linux systems (on
Linux, that file is contrary to its name not a shared library but an ASCII
linker script that loads a real `libc.so` file.)
@@ -104,11 +104,10 @@ assume that you accidentally define `atoi` as a global function in your
executable that behaves completely differently from the one in the C standard.
Then, all `atoi` function calls from any modules (even function calls within
libc) are redirected to your function instead of the one in libc, which will
very likely cause a problem.
That is a somewhat surprising consequence for an accidental name conflict. On
the other hand, this semantic is sometimes useful because it allows users to
override library functions without rebuilding modules containing them.
very likely cause a problem. That is a somewhat surprising consequence for an
accidental name conflict. On the other hand, this semantic is sometimes useful
because it allows users to override library functions without rebuilding
modules containing them.
Whether good or bad, you should keep these semantics in mind to understand
Unix linkers' behaviors.
@@ -117,7 +116,7 @@ Unix linkers' behaviors.
`mold`'s output is deterministic. That is, if you pass the same object files
and the same command-line options to the same version of `mold`, it is
guaranteed that `mold` produces the bit-by-bit identical output. The linker's
guaranteed that `mold` produces the bit-for-bit identical output. The linker's
internal randomness, such as the timing of thread scheduling or iteration
orders of hash tables, doesn't affect the output.
@@ -126,6 +125,24 @@ GNU linkers, for which some configurable values, such as system-dependent
library search paths, are hard-coded. `mold` depends only on its command-line
arguments.
## OPTION NOTATIONS
Long options are spelled with two dashes. Some also accept a single dash,
but which ones do is somewhat arbitrary for historical reasons. `mold`
follows the conventions of other linkers for compatibility. For example,
`--export-dynamic` can be spelled `-export-dynamic`, while `--execute-only`
requires two dashes.
This can cause unexpected behavior when constructing command-line options in
a script. For example, you might use `"-e$foo"` to set the entry point symbol
to the value of `$foo`. However, if `$foo` is `xport-dynamic`, the argument
becomes `-export-dynamic` and is interpreted as `--export-dynamic`.
To avoid such ambiguities, we recommend always using two dashes for long
options and separating a single-letter option from its argument with a
space. For example, use `-e $foo` or `--entry=$foo` to specify the entry
point symbol in a script.
## MOLD-SPECIFIC OPTIONS
* `--chroot`=_dir_:
@@ -142,11 +159,18 @@ arguments.
* `--no-color-diagnostics`:
Synonym for `--color-diagnostics=never`.
* `--detach`, `--no-detach`:
Permit or do not permit mold to create a debug info file in the background.
* `--fork`, `--no-fork`:
Spawn a child process and let it do the actual linking. When linking a large
program, the OS kernel can take a few hundred milliseconds to terminate a
`mold` process. `--fork` hides that latency. By default, it does fork.
Note that `--fork` also hides the resource usage statistics reported by
time(2), since it doesn't call waitpid(2) on the child process. If you
need those statistics, pass `--no-fork`.
* `--perf`:
Print performance statistics.
@@ -158,6 +182,15 @@ arguments.
object file in a static archive got linked or why some shared library is
kept in an output file's dependency list even with `--as-needed`.
* `--relocatable-merge-sections`:
By default, `mold` doesn't merge input sections by name when merging input
object files into a single output object file for `-r`. For example,
`.text.foo` and `.text.bar` aren't merged for `-r` even though they are
merged into `.text` based on the default section merging rules.
This option changes the behavior so that `mold` merges input sections by
name by the default section merging rules.
* `--repro`:
Archive input files, as well as a text file containing command line options,
in a tar file so that you can run `mold` with the exact same inputs again.
@@ -179,10 +212,29 @@ arguments.
easily test that your program works in the reversed initialization order.
* `--run` _command_ _arg_...:
Run _command_ with `mold` `/usr/bin/ld`. Specifically, `mold` runs a given
command with the `LD_PRELOAD` environment set to intercept exec(3) family
functions and replaces `argv[0]` with itself if it is `ld`, `ld.gold`, or
`ld.lld`.
Run _command_ with `mold` as `/usr/bin/ld`. Specifically, `mold` runs a
given command with the `LD_PRELOAD` environment set to intercept exec(3)
family functions and replaces `argv[0]` with itself if it is `ld`,
`ld.gold`, or `ld.lld`.
* `--separate-debug-file`, `--separate-debug-file`=_file_:
Bundle debug info sections into a separate file instead of embedding them in
an output executable or a shared library. mold creates a debug info file in
the background by default, so that you can start running your executable as
soon as possible.
By default, the debug info file is created in the same directory as is the
output file, with the `.dbg` file extension. That filename is embedded into
the output file so that `gdb` can automatically find the debug info file for
the output file. For more info about gdb features related to separate debug
files, see
<https://sourceware.org/gdb/current/onlinedocs/gdb.html/Separate-Debug-Files.html>.
mold holds a file lock with flock(2) while creating a debug info file in the
background.
If you don't want to create a debug info file in the background, pass the
`--no-detach` option.
* `--shuffle-sections`, `--shuffle-sections`=_number_:
Randomize the output by shuffling the order of input sections before
@@ -203,6 +255,15 @@ arguments.
`--shuffle-sections`, you can isolate your program's real performance number
from the randomness caused by memory layout changes.
* `--spare-program-headers`=_number_:
Append the given number of `PT_NULL` entries to the end of the program
header, so that post-link processing tools can easily add new segments by
overwriting the null entries.
Note that ELF requires all `PT_LOAD` segments to be sorted by `p_vaddr`.
Therefore, if you add a new LOAD segment, you may need to sort the entire
program header.
* `--stats`:
Print input statistics.
@@ -218,6 +279,60 @@ arguments.
* `--quick-exit`, `--no-quick-exit`:
Use or do not use `quick_exit` to exit.
* `--zero-to-bss`:
Convert all-zero data sections into BSS.
When this option is enabled, `mold` scans input data sections that are not
of type `SHT_NOBITS` and checks whether their contents consist solely of
zero bytes. Such sections are then converted into BSS (`SHT_NOBITS`)
sections. This reduces the size of the output file, since BSS sections
occupy no space in the file image.
This behavior is especially useful for user-defined sections created with
`__attribute__((section(".sectname")))` that contain uninitialized global
variables. GCC and Clang do not automatically mark such sections as BSS
even if their contents are entirely zero, and instead emit them as regular
data sections.
For example, consider `__attribute__((section(".sectname"))) int vec[256];`.
By default, this results in a `.sectname` section of type `SHT_PROGBITS`
filled with zeros. With `--zero-to-bss`, the linker will recognize it as
empty data and convert it to a `SHT_NOBITS` section, reducing the output
file size without changing runtime semantics.
* `-z rewrite-endbr`, `-z norewrite-endbr`:
As a security measure, some CPU instruction sets have recently gained a
feature to protect control flow integrity by disallowing indirect branches
by default. If the feature is enabled, the instruction that is executed
immediately after an indirect branch must be an branch target marker
instruction, or a CPU-level fault will raise. The marker instruction is also
known as "landing pad" instruction, to which indirect branches can land.
This feature makes ROP attacks harder to conduct.
To use the feature, a function whose pointer is taken needs to begin with a
landing pad because a function call via a function pointer is compiled to an
indirect branch. On the other hand, if a function is called only directly
(i.e. referred to only by _direct_ branch instructions), it doesn't have to
begin with it.
By default, the compiler always emits a landing pad at the beginning of each
global function because it doesn't know whether or not the function's
pointer is taken in another translation unit. As a result, the resulting
binary has more attack surface than necessary.
If `--rewrite-endbr` is given, mold conducts a whole program analysis
to identify functions whose addresses are actually taken and rewrites
landing pads with no-ops for non-address-taken functions, reducing the
attack surface.
This feature is currently available only on x86-64 and arm64.
On arm64, the landing pad is `bti c`. Note that code compiled with
`-mbranch-protection=standard` (BTI + pointer authentication) starts
functions with `paciasp`, which doubles as a landing pad, so there is no
separate `bti c` to rewrite and this option has no effect on such code.
## GNU-COMPATIBLE OPTIONS
* `--help`:
@@ -273,7 +388,14 @@ arguments.
Discard temporary local symbols to reduce the sizes of the symbol table and
the string table. Temporary local symbols are local symbols starting with
`.L`. Compilers usually generate such symbols for unnamed program elements
such as string literals or floating-point literals.
such as string literals or floating-point literals. This is the default;
use `--discard-none` to keep temporary local symbols.
* `--discard-none`:
Keep all local symbols in the symbol table, including temporary ones
starting with `.L`, except ones in mergeable sections. This restores the
default behavior of GNU ld. `mold` and `ld.lld` discard temporary local
symbols by default.
* `-e` _symbol_, `--entry`=_symbol_:
@@ -303,15 +425,6 @@ arguments.
object files to generate another object file that can be used as an input to
a linker.
* `--relocatable-merge-sections`:
By default, `mold` doesn't merge input sections by name when merging input
object files into a single output object file for `-r`. For example,
`.text.foo` and `.text.bar` aren't merged for `-r` even though they are
merged into `.text` according to the default section merging rules.
This option changes the behavior so that `mold` merges input sections by
name by the default section merging rules.
* `-s`, `--strip-all`:
Omit `.symtab` section from the output file.
@@ -340,8 +453,18 @@ arguments.
This option has the same effect as `--Bsymbolic` but works only for function
symbols. Data symbols remain being both imported and exported.
* `--Bsymbolic-non-weak`:
This option has the same effect as `--Bsymbolic` but works only for non-weak
symbols. Weak symbols remain being both imported and exported.
* `--Bsymbolic-non-weak-functions`:
This option has the same effect as `--Bsymbolic` but works only for non-weak
function symbols. Data symbols and weak function symbols remain being both
imported and exported.
* `--Bno-symbolic`:
Cancel `--Bsymbolic` and `--Bsymbolic-functions`.
Cancel `--Bsymbolic`, `--Bsymbolic-functions`, `--Bsymbolic-non-weak` and
`--Bsymbolic-non-weak-functions`.
* `--Map`=_file_:
Write map file to _file_.
@@ -355,12 +478,32 @@ arguments.
* `--Ttext`=_address_:
Alias for `--section-start=.text=`_address_.
* `--Ttext-segment`=_address_:
Set the address of the first byte of the text segment.
* `--allow-multiple-definition`:
Normally, the linker reports an error if there are more than one definition
of a symbol. This option changes the default behavior so that it doesn't
report an error for duplicate definitions and instead use the first
definition.
* `--allow-shlib-undefined`, `--no-allow-shlib-undefined`:
Even if mold succeeds in linking a main executable without undefined symbol
errors, you may still encounter symbol lookup errors at runtime because the
dynamic linker cannot find some symbols in shared libraries in any ELF
module. This occurs because mold ignores undefined symbols in shared
libraries by default.
If you pass `--no-allow-shlib-undefined`, mold verifies that undefined
symbols in shared libraries given to the linker can be resolved at
link-time. In other words, this converts the runtime error to a link-time
error.
Note that you need to pass all shared libraries, including indirectly
dependent ones, to the linker as arguments for `-l`. If a shared library
depends on a library that's not passed to the linker, the verification will
be skipped for that file.
* `--as-needed`, `--no-as-needed`:
By default, shared libraries given to the linker are unconditionally added
to the list of required libraries in an output file. However, shared
@@ -372,13 +515,13 @@ arguments.
The `--no-as-needed` option restores the default behavior for subsequent
files.
* `--build-id`=[ `md5` | `sha1` | `sha256` | `uuid` | `0x`_hexstring_ | `none` ]:
* `--build-id`=[ `md5` | `sha1` | `sha256` | `fast` | `uuid` | `0x`_hexstring_ | `none` ]:
Create a `.note.gnu.build-id` section containing a byte string to uniquely
identify an output file. `sha256` compute a 256-bit cryptographic hash of an
output file and set it to build-id. `md5` and `sha1` compute the same hash
but truncate it to 128 and 160 bits, respectively, before setting it to
build-id. `uuid` sets a random 128-bit UUID. `0x`_hexstring_ sets
_hexstring_.
_hexstring_. `fast` is a synonym for `sha256`.
* `--build-id`:
Synonym for `--build-id=sha256`.
@@ -386,9 +529,14 @@ arguments.
* `--no-build-id`:
Synonym for `--build-id=none`.
* `--compress-debug-sections`=[ `zlib` | `zlib-gabi` | `zstd` | `none` ]:
* `--compress-debug-sections`=[ `zlib` | `zlib-gabi` | `zlib:`_N_ | `zstd` | `zstd:`_N_ | `none` ]:
Compress DWARF debug info (`.debug_*` sections) using the zlib or zstd
compression algorithm. `zlib-gabi` is an alias for `zlib`.
compression algorithm. `zlib-gabi` is an alias for `zlib`. `zlib:`_N_
selects the zlib compression level, where _N_ is between 0 and 9 (0
means no compression), and `zstd:`_N_ selects the zstd compression
level, where _N_ is between 1 and 22. Higher levels achieve better
compression ratios but are slower. `zlib` is equivalent to `zlib:1`
and `zstd` is equivalent to `zstd:3`.
* `--defsym`=_symbol_=_value_:
Define _symbol_ as an alias for _value_.
@@ -434,17 +582,22 @@ arguments.
`--disable-new-dtags`, `mold` emits `DT_RPATH` for `--rpath` instead.
* `--execute-only`:
Traditionally, most processors require both executable and readable bits to
1 to make the page executable, which allows machine code to be read as data
at runtime. This is actually what an attacker often does after gaining a
limited control of a process to find pieces of machine code they can use to
gain the full control of the process. As a mitigation, some recent
processors allows "execute-only" pages. If a page is execute-only, you can
call a function there as long as you know its address but can't read it as
data.
This option marks text segments execute-only. This option currently works
only on some ARM64 processors.
Traditionally, setting the executable bit to 1 for a memory page implies
that the page also become readable, which allows machine code to be read
as data at runtime. That is actually what an attacker often does after
gaining a limited control of a process to find pieces of machine code
they can use to gain the full control of the process. As a mitigation,
recent processors including some ARM64 ones allows "execute-only" pages.
If a page is execute-only, you can call a function there as long as you
know its address but can't read it as data.
This option marks text segments as execute-only by setting just the "X"
bit instead of "RX". Note that on most systems, the absence of the "R"
bit in the text segment serves just as a hint. If you run a program
linked with `--execute-only` on a processor that doesn't support
execute-only pages, your executable will likely still function normally,
but the text segment will remain readable.
* `--exclude-libs`=_libraries_ ...:
Mark all symbols in the given _libraries_ hidden.
@@ -518,21 +671,47 @@ arguments.
* `--noinhibit-exec`:
Create an output file even if errors occur.
* `--pack-dyn-relocs`=[ `relr` | `none` ]:
* `--package-metadata`=_percent-encoded-string_:
Embed a specified string into the `.note.package` section. This option
is designed for build scripts that generate binary packages, such as
`.rpm` or `.deb`, to include package metadata in each executable. It
simplifies the process of identifying the corresponding package for a
given executable or core file.
An argument to this option is treated as percent-encoded and decoded
before being inserted into the section, allowing you to avoid the use of
the comma (`,`) character in the argument. This is useful because the
compiler replaces all occurrences of commas in `-Wl,` with spaces before
forwarding them to the linker. Note that `mold` always interprets the
argument as percent-encoded, so you also need to escape all occurrences
of `%` as `%25`.
* `--pack-dyn-relocs`=[ `relr` | `android` | `android+relr` | `none` ]:
Compress dynamic relocations in the output file.
If `relr` is specified, all `R_*_RELATIVE` relocations are put into
`.relr.dyn` section instead of `.rel.dyn` or `.rela.dyn` section. Since
`.relr.dyn` section uses a space-efficient encoding scheme, specifying this
flag can reduce the size of the output. This is typically most effective for
position-independent executable.
Note that a runtime loader has to support `.relr.dyn` to run executables or
shared libraries linked with `--pack-dyn-relocs=relr`. As of 2022, only
ChromeOS, Android and Fuchsia support it.
If `android` is specified, all dynamic relocations are encoded in Android's
packed relocation format (APS2) and stored in the `.rel.dyn` or `.rela.dyn`
section, whose section type is changed to `SHT_ANDROID_REL` or
`SHT_ANDROID_RELA`. The dynamic table is updated to use `DT_ANDROID_REL`
/ `DT_ANDROID_RELA` and their `SZ` variants in place of the standard tags.
* `--package-metadata`=_string_:
Embed _string_ to a `.note.package` section. This option is intended to be
used by a package management command such as rpm(8) to embed metadata
regarding a package to each executable file.
`android+relr` combines both: `R_*_RELATIVE` relocations go to `.relr.dyn`
and the rest are APS2-encoded. This is the configuration used by most
Android system binaries.
Note that a runtime loader has to support `.relr.dyn` to run executables or
shared libraries linked with `--pack-dyn-relocs=relr`. As of 2026, glibc
(since 2.36), musl (since 1.2.4), Android, ChromeOS, and Fuchsia all
support it. The APS2 format, on the other hand, is only handled by
Android's bionic loader; the encoder itself is target-agnostic, but the
produced binary can only be loaded on platforms where bionic (or a
compatible loader) handles `DT_ANDROID_REL(A)`.
* `--pie`, `--pic-executable`, `--no-pie`, `--no-pic-executable`:
Create a position-independent executable.
@@ -582,10 +761,9 @@ arguments.
Create a share library.
* `--spare-dynamic-tags`=_number_:
Reserve the given _number_ of tags in `.dynamic` section.
* `--spare-program-headers`=_number_:
Reserve the given _number_ of entries in the program header.
Append the given number of `DT_NULL` entries to the end of the `.dynamic`
section, so that post-link processing tools can easily add new dynamic tags
by overwriting the null entries.
* `--start-lib`, `--end-lib`:
Handle object files between `--start-lib` and `--end-lib` as if they were in
@@ -603,6 +781,10 @@ arguments.
* `--trace`:
Print name of each input file.
* `--undefined-glob`=_pattern_:
Synonym for `--undefined`, except that `--undefined-glob` takes a glob
pattern instead of just a single symbol name.
* `--undefined-version`, `--no-undefined-version`:
By default, `mold` warns on a symbol specified by a version script or by
`--export-dynamic-symbol` if it is not defined. You can silence the warning
@@ -619,6 +801,9 @@ arguments.
directory, it is searched from library search paths for the sake of
compatibility with GNU ld.
* `-w`, `--no-warnings`:
Suppress warnings and cancel `--fatal-warnings`.
* `--warn-common`, `--no-warn-common`:
Warn about common symbols.
@@ -690,12 +875,14 @@ arguments.
`.text.exit` as separate sections in the final binary instead of merging
them as `.text`.
* `-z rodynamic`:
Make the `.dynamic` section read-only.
* `-z relro`, `-z norelro`:
Some sections such as `.dynamic` have to be writable only during an
executable or a shared library file is being loaded to memory. Once the
dynamic linker finishes its job, such sections won't be mutated by anyone.
As a security mitigation, it is preferred to make such segments read-only
during program execution.
Some sections such as `.dynamic` have to be writable only during a module is
being loaded to memory. Once the dynamic linker finishes its job, such
sections won't be mutated by anyone. As a security mitigation, it is
preferred to make such segments read-only during program execution.
`-z relro` puts such sections into a special segment called `relro`. The
dynamic linker makes a relro segment read-only after it finishes its job.
@@ -714,14 +901,15 @@ arguments.
segments with two different attributes.
`separate-loadable-segments` adds paddings between segments with different
attributes so that they do not share the same page. This is the default.
attributes so that they do not share the same page.
`separate-code` adds paddings only between executable and non-executable
segments.
`noseparate-code` does not add any paddings between segments.
`noseparate-code` does not add any paddings between segments. This is the
default.
* `-z defs`, `-z nodefs`:
* `-z defs`, `-z undefs`:
Report undefined symbols (even with `--shared`).
* `-z shstk`:
@@ -730,6 +918,15 @@ arguments.
Control-flow Enforcement Technology (CET), which is available since Tiger
Lake (2020).
* `-z start-stop-visibility`=[ `hidden` | `protected` ]:
If a section name is valid as a C identifier (i.e., it matches
`/^[_a-zA-Z][_a-zA-Z0-9]*$/`), mold creates `__start_SECNAME` and
`__stop_SECNAME` symbols to mark the beginning and end of the section,
where `SECNAME` is the section name.
You can make these marker symbols visible from other ELF modules by passing
`-z start-stop-visibility=protected`. Default is `hidden`.
* `-z text`, `-z notext`, `-z textoff`:
`mold` by default reports an error if dynamic relocations are created in
read-only sections. If `-z notext` or `-z textoff` are given, `mold` creates
@@ -737,9 +934,11 @@ arguments.
default behavior.
* `-z max-page-size`=_number_:
Some CPU ISAs support multiple different memory page sizes. This option
specifies the maximum page size that an output binary can run on. The
default value is 4 KiB for i386, x86-64, and RISC-V, and 64 KiB for ARM64.
Some CPU ISAs support multiple memory page sizes. This option specifies the
maximum page size that an output binary can run on. In general, binaries
built for a larger page size can run on a system with a smaller page size,
but not vice versa. The default value is 4 KiB for i386, x86-64, and RISC-V,
and 64 KiB for ARM64.
* `-z nodefaultlib`:
Make the dynamic loader ignore default search paths.
@@ -764,7 +963,7 @@ arguments.
* `-z interpose`:
Mark object to interpose all DSOs but executable.
* `-(`, `-)`, `-EL`, `-O`_number_, `--allow-shlib-undefined`, `--dc`, `--dp`, `--end-group`, `--no-add-needed`, `--no-allow-shlib-undefined`, `--no-copy-dt-needed-entries`, `--no-fatal-warnings`, `--nostdlib`, `--rpath-link=Ar dir`, `--sort-common`, `--sort-section`, `--start-group`, `--warn-constructors`, `--warn-once`, `--fix-cortex-a53-835769`, `--fix-cortex-a53-843419`, `-z combreloc`, `-z common-page-size`, `-z nocombreloc`:
* `-(`, `-)`, `-EL`, `-O`_number_, `--dc`, `--dp`, `--end-group`, `--no-add-needed`, `--no-copy-dt-needed-entries`, `--nostdlib`, `--rpath-link=Ar dir`, `--sort-common`, `--sort-section`, `--start-group`, `--warn-constructors`, `--warn-once`, `--fix-cortex-a53-835769`, `--fix-cortex-a53-843419`, `-z combreloc`, `-z common-page-size`, `-z nocombreloc`:
Ignored
## ENVIRONMENT VARIABLES
-330
View File
@@ -1,330 +0,0 @@
// Alpha is a 64-bit RISC ISA developed by DEC (Digital Equipment
// Corporation) in the early '90s. It aimed to be an ISA that would last
// 25 years. DEC expected Alpha would become 1000x faster during that time
// span. Since the ISA was developed from scratch for future machines,
// it's 64-bit from the beginning. There's no 32-bit variant.
//
// DEC ported its own Unix (Tru64) to Alpha. Microsoft also ported Windows
// NT to it. But it wasn't a huge commercial success.
//
// DEC was acquired by Compaq in 1997. In the late '90s, Intel and
// Hewlett-Packard were advertising that their upcoming Itanium processor
// would achieve significantly better performance than RISC processors, so
// Compaq decided to discontinue the Alpha processor line to switch to
// Itanium. Itanium resulted in a miserable failure, but it still suceeded
// to wipe out several RISC processors just by promising overly optimistic
// perf numbers. Alpha as an ISA would probably have been fine after 25
// years since its introduction (which is 1992 + 25 = 2017), but the
// company and its market didn't last that long.
//
// From the linker's point of view, there are a few peculiarities in its
// psABI as shown below:
//
// - Alpha lacks PC-relative memory load/store instructions, so it uses
// register-relative load/store instructions in position-independent
// code. Specifically, GP (which is an alias for $r29) is always
// maintained to refer to .got+0x8000, and global variables' addresses
// are loaded in a GP-relative manner.
//
// - It looks like even function addresses are first loaded to register
// in a GP-relative manner before calling it. We can relax it to
// convert the instruction sequence with a direct branch instruction,
// but by default, object files don't use a direct branch to call a
// function. Therefore, by default, we don't need to create a PLT.
// Any function call is made by first reading its address from GOT and
// jump to the address.
#include "mold.h"
namespace mold::elf {
using E = ALPHA;
// A 32-bit immediate can be materialized in a register with a "load high"
// and a "load low" instruction sequence. The first instruction sets the
// upper 16 bits in a register, and the second one set the lower 16
// bits. When doing so, they sign-extend an immediate. Therefore, if the
// 15th bit of an immediate happens to be 1, setting a "low half" value
// negates the upper 16 bit values that has already been set in a
// register. To compensate that, we need to add 0x8000 when setting the
// upper 16 bits.
static u32 hi(u32 val) {
return bits(val + 0x8000, 31, 16);
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_ALPHA_SREL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
u64 GP = ctx.got->shdr.sh_addr + 0x8000;
switch (rel.r_type) {
case R_ALPHA_REFQUAD:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_ALPHA_GPREL32:
*(ul32 *)loc = S + A - GP;
break;
case R_ALPHA_LITERAL:
if (A)
*(ul16 *)loc = ctx.extra.got->get_addr(sym, A) - GP;
else
*(ul16 *)loc = GOT + G - GP;
break;
case R_ALPHA_BRSGP:
*(ul32 *)loc |= bits(S + A - P - 4, 22, 0);
break;
case R_ALPHA_GPDISP:
*(ul16 *)loc = hi(GP - P);
*(ul16 *)(loc + A) = GP - P;
break;
case R_ALPHA_SREL32:
*(ul32 *)loc = S + A - P;
break;
case R_ALPHA_GPRELHIGH:
*(ul16 *)loc = hi(S + A - GP);
break;
case R_ALPHA_GPRELLOW:
*(ul16 *)loc = S + A - GP;
break;
case R_ALPHA_TLSGD:
*(ul16 *)loc = sym.get_tlsgd_addr(ctx) - GP;
break;
case R_ALPHA_TLSLDM:
*(ul16 *)loc = ctx.got->get_tlsld_addr(ctx) - GP;
break;
case R_ALPHA_DTPRELHI:
*(ul16 *)loc = hi(S + A - ctx.dtp_addr);
break;
case R_ALPHA_DTPRELLO:
*(ul16 *)loc = S + A - ctx.dtp_addr;
break;
case R_ALPHA_GOTTPREL:
*(ul16 *)loc = sym.get_gottp_addr(ctx) + A - GP;
break;
case R_ALPHA_TPRELHI:
*(ul16 *)loc = hi(S + A - ctx.tp_addr);
break;
case R_ALPHA_TPRELLO:
*(ul16 *)loc = S + A - ctx.tp_addr;
break;
case R_ALPHA_LITUSE:
case R_ALPHA_HINT:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_ALPHA_REFLONG:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
case R_ALPHA_REFQUAD:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on Alpha";
switch (rel.r_type) {
case R_ALPHA_REFQUAD:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_ALPHA_LITERAL:
if (rel.r_addend)
ctx.extra.got->add_symbol(sym, rel.r_addend);
else
sym.flags |= NEEDS_GOT;
break;
case R_ALPHA_SREL32:
scan_pcrel(ctx, sym, rel);
break;
case R_ALPHA_BRSGP:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ALPHA_TLSGD:
sym.flags |= NEEDS_TLSGD;
break;
case R_ALPHA_TLSLDM:
ctx.needs_tlsld = true;
break;
case R_ALPHA_GOTTPREL:
sym.flags |= NEEDS_GOTTP;
break;
case R_ALPHA_TPRELHI:
case R_ALPHA_TPRELLO:
check_tlsle(ctx, sym, rel);
break;
case R_ALPHA_GPREL32:
case R_ALPHA_LITUSE:
case R_ALPHA_GPDISP:
case R_ALPHA_HINT:
case R_ALPHA_GPRELHIGH:
case R_ALPHA_GPRELLOW:
case R_ALPHA_DTPRELHI:
case R_ALPHA_DTPRELLO:
break;
default:
Fatal(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
// An R_ALPHA_LITERAL relocation may request the linker to create a GOT
// entry for an external symbol with a non-zero addend. This is an unusual
// request which is not found in any other targets.
//
// Referring an external symbol with a non-zero addend is a bad practice
// because we need to create as many dynamic relocations as the number of
// distinctive addends for the same symbol.
//
// We don't want to mess up the implementation of the common GOT section
// for Alpha. So we create another GOT-like section, .alpha_got. Any GOT
// entry for an R_ALPHA_LITERAL reloc with a non-zero addend is created
// not in .got but in .alpha_got.
//
// Since .alpha_got entries are accessed relative to GP, .alpha_got
// needs to be close enough to .got. It's actually placed next to .got.
void AlphaGotSection::add_symbol(Symbol<E> &sym, i64 addend) {
assert(addend);
std::scoped_lock lock(mu);
entries.push_back({&sym, addend});
}
bool operator<(const AlphaGotSection::Entry &a, const AlphaGotSection::Entry &b) {
return std::tuple(a.sym->file->priority, a.sym->sym_idx, a.addend) <
std::tuple(b.sym->file->priority, b.sym->sym_idx, b.addend);
};
u64 AlphaGotSection::get_addr(Symbol<E> &sym, i64 addend) {
auto it = std::lower_bound(entries.begin(), entries.end(), Entry{&sym, addend});
assert(it != entries.end());
return this->shdr.sh_addr + (it - entries.begin()) * sizeof(Word<E>);
}
i64 AlphaGotSection::get_reldyn_size(Context<E> &ctx) const {
i64 n = 0;
for (const Entry &e : entries)
if (e.sym->is_imported || (ctx.arg.pic && !e.sym->is_absolute()))
n++;
return n;
}
void AlphaGotSection::finalize() {
sort(entries);
remove_duplicates(entries);
shdr.sh_size = entries.size() * sizeof(Word<E>);
}
void AlphaGotSection::copy_buf(Context<E> &ctx) {
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
reldyn_offset);
for (i64 i = 0; i < entries.size(); i++) {
Entry &e = entries[i];
u64 P = this->shdr.sh_addr + sizeof(Word<E>) * i;
ul64 *buf = (ul64 *)(ctx.buf + this->shdr.sh_offset + sizeof(Word<E>) * i);
if (e.sym->is_imported) {
*buf = ctx.arg.apply_dynamic_relocs ? e.addend : 0;
*dynrel++ = ElfRel<E>(P, E::R_ABS, e.sym->get_dynsym_idx(ctx), e.addend);
} else {
*buf = e.sym->get_addr(ctx) + e.addend;
if (ctx.arg.pic && !e.sym->is_absolute())
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, *buf);
}
}
}
} // namespace mold::elf
-786
View File
@@ -1,786 +0,0 @@
// ARM32 is a bit special from the linker's viewpoint because ARM
// processors support two different instruction encodings: Thumb and
// ARM (in a narrower sense). Thumb instructions are either 16 bits or
// 32 bits, while ARM instructions are all 32 bits. Feature-wise,
// Thumb is a subset of ARM, so not all ARM instructions are
// representable in Thumb.
//
// ARM processors originally supported only ARM instructions. Thumb
// instructions were later added to increase code density.
//
// ARM processors runs in either ARM mode or Thumb mode. The mode can
// be switched using BX (branch and mode exchange)-family instructions.
// We need to use that instructions to, for example, call a function
// encoded in Thumb from a function encoded in ARM. Sometimes, the
// linker even has to emit interworking thunk code to switch mode.
//
// ARM instructions are aligned to 4 byte boundaries. Thumb are to 2
// byte boundaries. So the least significant bit of a function address
// is always 0.
//
// To distinguish Thumb functions from ARM fucntions, the LSB of a
// function address is repurposed as a boolean flag. If the LSB is 0,
// the function referred to by the address is encoded in ARM;
// otherwise, Thumb.
//
// For example, if a symbol `foo` is of type STT_FUNC and has value
// 0x2001, `foo` is a function using Thumb instructions whose address
// is 0x2000 (not 0x2001, as Thumb instructions are always 2-byte
// aligned). Likewise, if a function pointer has value 0x2001, it
// refers a Thumb function at 0x2000.
//
// https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h>
namespace mold::elf {
using E = ARM32;
template <>
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_REL32:
case R_ARM_TARGET1:
case R_ARM_BASE_PREL:
case R_ARM_GOTOFF32:
case R_ARM_GOT_PREL:
case R_ARM_GOT_BREL:
case R_ARM_TLS_GD32:
case R_ARM_TLS_LDM32:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LE32:
case R_ARM_TLS_GOTDESC:
case R_ARM_TARGET2:
return *(il32 *)loc;
case R_ARM_THM_JUMP11:
return sign_extend(*(ul16 *)loc, 10) << 1;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
case R_ARM_THM_TLS_CALL: {
u32 S = bit(*(ul16 *)loc, 10);
u32 J1 = bit(*(ul16 *)(loc + 2), 13);
u32 J2 = bit(*(ul16 *)(loc + 2), 11);
u32 I1 = !(J1 ^ S);
u32 I2 = !(J2 ^ S);
u32 imm10 = bits(*(ul16 *)loc, 9, 0);
u32 imm11 = bits(*(ul16 *)(loc + 2), 10, 0);
u32 val = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
return sign_extend(val, 24);
}
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PLT32:
case R_ARM_TLS_CALL:
return sign_extend(*(ul32 *)loc, 23) << 2;
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_PREL:
case R_ARM_MOVT_ABS: {
u32 imm12 = bits(*(ul32 *)loc, 11, 0);
u32 imm4 = bits(*(ul32 *)loc, 19, 16);
return sign_extend((imm4 << 12) | imm12, 15);
}
case R_ARM_PREL31:
return sign_extend(*(ul32 *)loc, 30);
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_PREL:
case R_ARM_THM_MOVT_ABS: {
u32 imm4 = bits(*(ul16 *)loc, 3, 0);
u32 i = bit(*(ul16 *)loc, 10);
u32 imm3 = bits(*(ul16 *)(loc + 2), 14, 12);
u32 imm8 = bits(*(ul16 *)(loc + 2), 7, 0);
u32 val = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
return sign_extend(val, 15);
}
default:
return 0;
}
}
static void write_mov_imm(u8 *loc, u32 val) {
u32 imm12 = bits(val, 11, 0);
u32 imm4 = bits(val, 15, 12);
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
}
static void write_thm_b_imm(u8 *loc, u32 val) {
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate-
u32 sign = bit(val, 24);
u32 I1 = bit(val, 23);
u32 I2 = bit(val, 22);
u32 J1 = !I1 ^ sign;
u32 J2 = !I2 ^ sign;
u32 imm10 = bits(val, 21, 12);
u32 imm11 = bits(val, 11, 1);
ul16 *buf = (ul16 *)loc;
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (sign << 10) | imm10;
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
}
static void write_thm_mov_imm(u8 *loc, u32 val) {
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/MOVT
u32 imm4 = bits(val, 15, 12);
u32 i = bit(val, 11);
u32 imm3 = bits(val, 10, 8);
u32 imm8 = bits(val, 7, 0);
ul16 *buf = (ul16 *)loc;
buf[0] = (buf[0] & 0b1111'1011'1111'0000) | (i << 10) | imm4;
buf[1] = (buf[1] & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
}
template <>
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_ARM_NONE:
break;
case R_ARM_ABS32:
case R_ARM_REL32:
case R_ARM_TARGET1:
case R_ARM_BASE_PREL:
case R_ARM_GOTOFF32:
case R_ARM_GOT_PREL:
case R_ARM_GOT_BREL:
case R_ARM_TLS_GD32:
case R_ARM_TLS_LDM32:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_IE32:
case R_ARM_TLS_LE32:
case R_ARM_TLS_GOTDESC:
case R_ARM_TARGET2:
*(ul32 *)loc = val;
break;
case R_ARM_THM_JUMP11:
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | bits(val, 11, 1);
break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
case R_ARM_THM_TLS_CALL:
write_thm_b_imm(loc, val);
break;
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PLT32:
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
break;
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_PREL:
case R_ARM_MOVT_ABS:
write_mov_imm(loc, val);
break;
case R_ARM_PREL31:
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
break;
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_PREL:
case R_ARM_THM_MOVT_ABS:
write_thm_mov_imm(loc, val);
break;
default:
unreachable();
}
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul32 insn[] = {
0xe52d'e004, // push {lr}
0xe59f'e004, // ldr lr, 2f
0xe08f'e00e, // 1: add lr, pc, lr
0xe5be'f008, // ldr pc, [lr, #8]!
0x0000'0000, // 2: .word .got.plt - 1b - 8
0x0000'0000, // (padding)
0x0000'0000, // (padding)
0x0000'0000, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
}
static const ul32 plt_entry[] = {
0xe59f'c004, // 1: ldr ip, 2f
0xe08c'c00f, // add ip, ip, pc
0xe59c'f000, // ldr pc, [ip]
0x0000'0000, // 2: .word sym@GOT - 1b
};
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ul32 *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_ARM_ABS32:
*(ul32 *)loc = val;
break;
case R_ARM_REL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
// ARM and Thumb branch instructions can jump within ±16 MiB.
static bool is_jump_reachable(i64 val) {
return sign_extend(val, 24) == val;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
auto get_tls_trampoline_addr = [&, i = 0](u64 addr) mutable {
for (; i < output_section->thunks.size(); i++) {
i64 disp = output_section->shdr.sh_addr + output_section->thunks[i]->offset -
addr;
if (is_jump_reachable(disp))
return disp;
}
unreachable();
};
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || rel.r_type == R_ARM_V4BX)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
u64 S = sym.get_addr(ctx);
u64 A = get_addend(*this, rel);
u64 P = get_addr() + rel.r_offset;
u64 T = S & 1;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
auto get_thumb_thunk_addr = [&] { return get_thunk_addr(i); };
auto get_arm_thunk_addr = [&] { return get_thunk_addr(i) + 4; };
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_TARGET1:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_ARM_REL32:
*(ul32 *)loc = S + A - P;
break;
case R_ARM_THM_CALL: {
if (sym.is_remaining_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(ul32 *)loc = 0x8000'f3af; // NOP.W
break;
}
// THM_CALL relocation refers either BL or BLX instruction.
// They are different in only one bit. We need to use BL if
// the jump target is Thumb. Otherwise, use BLX.
i64 val = S + A - P;
if (is_jump_reachable(val)) {
if (T) {
write_thm_b_imm(loc, val);
*(ul16 *)(loc + 2) |= 0x1000; // rewrite to BL
} else {
write_thm_b_imm(loc, align_to(val, 4));
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
}
} else {
write_thm_b_imm(loc, align_to(get_arm_thunk_addr() + A - P, 4));
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
}
break;
}
case R_ARM_BASE_PREL:
*(ul32 *)loc = GOT + A - P;
break;
case R_ARM_GOTOFF32:
*(ul32 *)loc = ((S + A) | T) - GOT;
break;
case R_ARM_GOT_PREL:
case R_ARM_TARGET2:
*(ul32 *)loc = GOT + G + A - P;
break;
case R_ARM_GOT_BREL:
*(ul32 *)loc = G + A;
break;
case R_ARM_CALL: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
break;
}
// Just like THM_CALL, ARM_CALL relocation refers to either BL or
// BLX instruction. We may need to rewrite BL → BLX or BLX → BL.
bool is_bl = ((*(ul32 *)loc & 0xff00'0000) == 0xeb00'0000);
bool is_blx = ((*(ul32 *)loc & 0xfe00'0000) == 0xfa00'0000);
if (!is_bl && !is_blx)
Fatal(ctx) << *this << ": R_ARM_CALL refers to neither BL nor BLX";
u64 val = S + A - P;
if (is_jump_reachable(val)) {
if (T) {
*(ul32 *)loc = 0xfa00'0000; // BLX
*(ul32 *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
} else {
*(ul32 *)loc = 0xeb00'0000; // BL
*(ul32 *)loc |= bits(val, 25, 2);
}
} else {
*(ul32 *)loc = 0xeb00'0000; // BL
*(ul32 *)loc |= bits(get_arm_thunk_addr() + A - P, 25, 2);
}
break;
}
case R_ARM_JUMP24: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
break;
}
// These relocs refers to a B (unconditional branch) instruction.
// Unlike BL or BLX, we can't rewrite B to BX in place when the
// processor mode switch is required because BX doesn't takes an
// immediate; it takes only a register. So if mode switch is
// required, we jump to a linker-synthesized thunk which does the
// job with a longer code sequence.
u64 val = S + A - P;
if (!is_jump_reachable(val) || T)
val = get_arm_thunk_addr() + A - P;
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
break;
}
case R_ARM_PLT32:
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
} else {
u64 val = (T ? get_arm_thunk_addr() : S) + A - P;
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
}
break;
case R_ARM_THM_JUMP11:
assert(T);
check(S + A - P, -(1 << 11), 1 << 11);
*(ul16 *)loc &= 0xf800;
*(ul16 *)loc |= bits(S + A - P, 11, 1);
break;
case R_ARM_THM_JUMP19: {
i64 val = S + A - P;
check(val, -(1 << 19), 1 << 19);
// sign:J2:J1:imm6:imm11:'0'
u32 sign = bit(val, 20);
u32 J2 = bit(val, 19);
u32 J1 = bit(val, 18);
u32 imm6 = bits(val, 17, 12);
u32 imm11 = bits(val, 11, 1);
*(ul16 *)loc &= 0b1111'1011'1100'0000;
*(ul16 *)loc |= (sign << 10) | imm6;
*(ul16 *)(loc + 2) &= 0b1101'0000'0000'0000;
*(ul16 *)(loc + 2) |= (J2 << 13) | (J1 << 11) | imm11;
break;
}
case R_ARM_THM_JUMP24: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0x8000'f3af; // NOP
break;
}
// Just like R_ARM_JUMP24, we need to jump to a thunk if we need to
// switch processor mode.
u64 val = S + A - P;
if (!is_jump_reachable(val) || !T)
val = get_thumb_thunk_addr() + A - P;
write_thm_b_imm(loc, val);
break;
}
case R_ARM_MOVW_PREL_NC:
write_mov_imm(loc, ((S + A) | T) - P);
break;
case R_ARM_MOVW_ABS_NC:
write_mov_imm(loc, (S + A) | T);
break;
case R_ARM_THM_MOVW_PREL_NC:
write_thm_mov_imm(loc, ((S + A) | T) - P);
break;
case R_ARM_PREL31:
check(S + A - P, -(1LL << 30), 1LL << 30);
*(ul32 *)loc &= 0x8000'0000;
*(ul32 *)loc |= (S + A - P) & 0x7fff'ffff;
break;
case R_ARM_THM_MOVW_ABS_NC:
write_thm_mov_imm(loc, (S + A) | T);
break;
case R_ARM_MOVT_PREL:
write_mov_imm(loc, (S + A - P) >> 16);
break;
case R_ARM_THM_MOVT_PREL:
write_thm_mov_imm(loc, (S + A - P) >> 16);
break;
case R_ARM_MOVT_ABS:
write_mov_imm(loc, (S + A) >> 16);
break;
case R_ARM_THM_MOVT_ABS:
write_thm_mov_imm(loc, (S + A) >> 16);
break;
case R_ARM_TLS_GD32:
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
break;
case R_ARM_TLS_LDM32:
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
break;
case R_ARM_TLS_LDO32:
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_ARM_TLS_IE32:
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_ARM_TLS_LE32:
*(ul32 *)loc = S + A - ctx.tp_addr;
break;
case R_ARM_TLS_GOTDESC:
// ARM32 TLSDESC uses the following code sequence to materialize
// a TP-relative address in r0.
//
// ldr r0, .L2
// .L1: bl foo
// R_ARM_TLS_CALL
// .L2: .word foo + . - .L1
// R_ARM_TLS_GOTDESC
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// ldr r0, .L2
// .L1: ldr r0, [pc, r0]
// ...
// .L2: .word foo(gottpoff) + . - .L1
//
// or to the following for executable.
//
// ldr r0, .L2
// .L1: nop
// ...
// .L2: .word foo(tpoff)
if (sym.has_tlsdesc(ctx)) {
// A is odd if the corresponding TLS_CALL is Thumb.
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
} else {
*(ul32 *)loc = S - ctx.tp_addr;
}
break;
case R_ARM_TLS_CALL:
if (sym.has_tlsdesc(ctx)) {
// BL <tls_trampoline>
*(ul32 *)loc = 0xeb00'0000 | bits(get_tls_trampoline_addr(P + 8), 25, 2);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
} else {
*(ul32 *)loc = 0xe320'f000; // nop
}
break;
case R_ARM_THM_TLS_CALL:
if (sym.has_tlsdesc(ctx)) {
u64 val = align_to(get_tls_trampoline_addr(P + 4), 4);
write_thm_b_imm(loc, val);
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
} else if (sym.has_gottp(ctx)) {
// Since `ldr r0, [pc, r0]` is not representable in Thumb,
// we use two instructions instead.
*(ul16 *)loc = 0x4478; // add r0, pc
*(ul16 *)(loc + 2) = 0x6800; // ldr r0, [r0]
} else {
*(ul32 *)loc = 0x8000'f3af; // nop.w
}
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : get_addend(*this, rel);
switch (rel.r_type) {
case R_ARM_ABS32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_MOVT_ABS:
case R_ARM_THM_MOVT_ABS:
case R_ARM_TARGET1:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_ARM_THM_CALL:
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PLT32:
case R_ARM_THM_JUMP24:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ARM_GOT_PREL:
case R_ARM_GOT_BREL:
case R_ARM_TARGET2:
sym.flags |= NEEDS_GOT;
break;
case R_ARM_MOVT_PREL:
case R_ARM_THM_MOVT_PREL:
case R_ARM_PREL31:
scan_pcrel(ctx, sym, rel);
break;
case R_ARM_TLS_GD32:
sym.flags |= NEEDS_TLSGD;
break;
case R_ARM_TLS_LDM32:
ctx.needs_tlsld = true;
break;
case R_ARM_TLS_IE32:
sym.flags |= NEEDS_GOTTP;
break;
case R_ARM_TLS_CALL:
case R_ARM_THM_TLS_CALL:
scan_tlsdesc(ctx, sym);
break;
case R_ARM_TLS_LE32:
check_tlsle(ctx, sym, rel);
break;
case R_ARM_REL32:
case R_ARM_BASE_PREL:
case R_ARM_GOTOFF32:
case R_ARM_THM_JUMP11:
case R_ARM_THM_JUMP19:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_TLS_LDO32:
case R_ARM_V4BX:
case R_ARM_TLS_GOTDESC:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void Thunk<E>::copy_buf(Context<E> &ctx) {
// TLS trampoline code. ARM32's TLSDESC is designed so that this
// common piece of code is factored out from object files to reduce
// output size. Since no one provide, the linker has to synthesize it.
static ul32 hdr[] = {
0xe08e'0000, // add r0, lr, r0
0xe590'1004, // ldr r1, [r0, #4]
0xe12f'ff11, // bx r1
0xe320'f000, // nop
};
// This is a range extension and mode switch thunk.
// It has two entry points: +0 for Thumb and +4 for ARM.
const u8 entry[] = {
// .thumb
0x78, 0x47, // bx pc # jumps to 1f
0xc0, 0x46, // nop
// .arm
0x00, 0xc0, 0x9f, 0xe5, // 1: ldr ip, 3f
0x0f, 0xf0, 0x8c, 0xe0, // 2: add pc, ip, pc
0x00, 0x00, 0x00, 0x00, // 3: .word sym - 2b
};
static_assert(E::thunk_hdr_size == sizeof(hdr));
static_assert(E::thunk_size == sizeof(entry));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
memcpy(buf, hdr, sizeof(hdr));
buf += sizeof(hdr);
u64 P = output_section.shdr.sh_addr + offset + sizeof(hdr);
for (Symbol<E> *sym : symbols) {
memcpy(buf, entry, sizeof(entry));
*(ul32 *)(buf + 12) = sym->get_addr(ctx) - P - 16;
buf += sizeof(entry);
P += sizeof(entry);
}
}
template <>
u64 get_eflags(Context<E> &ctx) {
return EF_ARM_EABI_VER5;
}
// ARM executables use an .ARM.exidx section to look up an exception
// handling record for the current instruction pointer. The table needs
// to be sorted by their addresses.
//
// Other target uses .eh_frame_hdr instead for the same purpose.
// I don't know why only ARM uses the different mechanism, but it's
// likely that it's due to some historical reason.
//
// This function sorts .ARM.exidx records.
void fixup_arm_exidx_section(Context<E> &ctx) {
Timer t(ctx, "fixup_arm_exidx_section");
OutputSection<E> *osec = find_section(ctx, SHT_ARM_EXIDX);
if (!osec)
return;
// .ARM.exidx records consists of a signed 31-bit relative address
// and a 32-bit value. The relative address indicates the start
// address of a function that the record covers. The value is one of
// the followings:
//
// 1. CANTUNWIND indicating that there's no unwinding info for the function,
// 2. a compact unwinding record encoded into a 32-bit value, or
// 3. a 31-bit relative address which points to a larger record in
// the .ARM.extab section.
//
// CANTUNWIND is value 1. The most significant bit is set in (2) but
// not in (3). So we can distinguished them just by looking at a value.
const u32 EXIDX_CANTUNWIND = 1;
struct Entry {
ul32 addr;
ul32 val;
};
if (osec->shdr.sh_size % sizeof(Entry))
Fatal(ctx) << "invalid .ARM.exidx section size";
Entry *ent = (Entry *)(ctx.buf + osec->shdr.sh_offset);
i64 num_entries = osec->shdr.sh_size / sizeof(Entry);
// Entry's addresses are relative to themselves. In order to sort
// records by addresses, we first translate them so that the addresses
// are relative to the beginning of the section.
auto is_relative = [](u32 val) {
return val != EXIDX_CANTUNWIND && !(val & 0x8000'0000);
};
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
i64 offset = sizeof(Entry) * i;
ent[i].addr = sign_extend(ent[i].addr, 30) + offset;
if (is_relative(ent[i].val))
ent[i].val = 0x7fff'ffff & (ent[i].val + offset);
});
tbb::parallel_sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
return a.addr < b.addr;
});
// Make addresses relative to themselves.
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
i64 offset = sizeof(Entry) * i;
ent[i].addr = 0x7fff'ffff & (ent[i].addr - offset);
if (is_relative(ent[i].val))
ent[i].val = 0x7fff'ffff & (ent[i].val - offset);
});
// .ARM.exidx's sh_link should be set to the .text section index.
// Runtime doesn't care about it, but the binutils's strip command does.
if (ctx.shdr) {
if (Chunk<E> *text = find_section(ctx, ".text")) {
osec->shdr.sh_link = text->shndx;
ctx.shdr->copy_buf(ctx);
}
}
}
} // namespace mold::elf
-629
View File
@@ -1,629 +0,0 @@
// This file contains ARM64-specific code. Being new, the ARM64's ELF
// psABI doesn't have anything peculiar. ARM64 is a clean RISC
// instruction set that supports PC-relative load/store instructions.
//
// Unlike ARM32, instructions length doesn't vary. All ARM64
// instructions are 4 bytes long.
//
// Branch instructions used for function call can jump within ±128 MiB.
// We need to create range extension thunks to support binaries whose
// .text is larger than that.
//
// Unlike most other targets, the TLSDESC access model is used by default
// for -fPIC to access thread-local variables instead of the less
// efficient GD model. You can still enable GD but it needs the
// -mtls-dialect=trad flag. Since GD is used rarely, we don't need to
// implement GD → LE relaxation.
//
// https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
#include "mold.h"
namespace mold::elf {
using E = ARM64;
static void write_adrp(u8 *buf, u64 val) {
*(ul32 *)buf |= (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
}
static void write_adr(u8 *buf, u64 val) {
*(ul32 *)buf |= (bits(val, 1, 0) << 29) | (bits(val, 20, 2) << 5);
}
static void write_movn_movz(u8 *buf, i64 val) {
*(ul32 *)buf &= 0b0000'0000'0110'0000'0000'0000'0001'1111;
if (val >= 0)
*(ul32 *)buf |= 0xd280'0000 | (bits(val, 15, 0) << 5); // rewrite to movz
else
*(ul32 *)buf |= 0x9280'0000 | (bits(~val, 15, 0) << 5); // rewrite to movn
}
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul32 insn[] = {
0xa9bf'7bf0, // stp x16, x30, [sp,#-16]!
0x9000'0010, // adrp x16, .got.plt[2]
0xf940'0211, // ldr x17, [x16, .got.plt[2]]
0x9100'0210, // add x16, x16, .got.plt[2]
0xd61f'0220, // br x17
0xd503'201f, // nop
0xd503'201f, // nop
0xd503'201f, // nop
};
u64 gotplt = ctx.gotplt->shdr.sh_addr + 16;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, insn, sizeof(insn));
write_adrp(buf + 4, page(gotplt) - page(plt + 4));
*(ul32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(buf + 12) |= (gotplt & 0xfff) << 10;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul32 insn[] = {
0x9000'0010, // adrp x16, .got.plt[n]
0xf940'0211, // ldr x17, [x16, .got.plt[n]]
0x9100'0210, // add x16, x16, .got.plt[n]
0xd61f'0220, // br x17
};
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, insn, sizeof(insn));
write_adrp(buf, page(gotplt) - page(plt));
*(ul32 *)(buf + 4) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(buf + 8) |= (gotplt & 0xfff) << 10;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul32 insn[] = {
0x9000'0010, // adrp x16, GOT[n]
0xf940'0211, // ldr x17, [x16, GOT[n]]
0xd61f'0220, // br x17
0xd503'201f, // nop
};
u64 got = sym.get_got_pltgot_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, insn, sizeof(insn));
write_adrp(buf, page(got) - page(plt));
*(ul32 *)(buf + 4) |= bits(got, 11, 3) << 10;
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_AARCH64_ABS64:
*(ul64 *)loc = val;
break;
case R_AARCH64_PREL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_AARCH64_PREL64:
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
static bool is_adrp(u8 *loc) {
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/ADRP--Form-PC-relative-address-to-4KB-page-
u32 insn = *(ul32 *)loc;
return (bits(insn, 31, 24) & 0b1001'1111) == 0b1001'0000;
}
static bool is_ldr(u8 *loc) {
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDR--immediate---Load-Register--immediate--
u32 insn = *(ul32 *)loc;
return (bits(insn, 31, 20) & 0b1111'1111'1100) == 0b1111'1001'0100;
}
static bool is_add(u8 *loc) {
// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/ADD--immediate---Add--immediate--
u32 insn = *(ul32 *)loc;
return (bits(insn, 31, 20) & 0b1111'1111'1100) == 0b1001'0001'0000;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_AARCH64_ABS64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_ADD_ABS_LO12_NC:
*(ul32 *)loc |= bits(S + A, 11, 0) << 10;
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
*(ul32 *)loc |= bits(S + A, 11, 1) << 10;
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
*(ul32 *)loc |= bits(S + A, 11, 2) << 10;
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
*(ul32 *)loc |= bits(S + A, 11, 3) << 10;
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
*(ul32 *)loc |= bits(S + A, 11, 4) << 10;
break;
case R_AARCH64_MOVW_UABS_G0:
check(S + A, 0, 1 << 16);
*(ul32 *)loc |= bits(S + A, 15, 0) << 5;
break;
case R_AARCH64_MOVW_UABS_G0_NC:
*(ul32 *)loc |= bits(S + A, 15, 0) << 5;
break;
case R_AARCH64_MOVW_UABS_G1:
check(S + A, 0, 1LL << 32);
*(ul32 *)loc |= bits(S + A, 31, 16) << 5;
break;
case R_AARCH64_MOVW_UABS_G1_NC:
*(ul32 *)loc |= bits(S + A, 31, 16) << 5;
break;
case R_AARCH64_MOVW_UABS_G2:
check(S + A, 0, 1LL << 48);
*(ul32 *)loc |= bits(S + A, 47, 32) << 5;
break;
case R_AARCH64_MOVW_UABS_G2_NC:
*(ul32 *)loc |= bits(S + A, 47, 32) << 5;
break;
case R_AARCH64_MOVW_UABS_G3:
*(ul32 *)loc |= bits(S + A, 63, 48) << 5;
break;
case R_AARCH64_ADR_GOT_PAGE:
if (sym.has_got(ctx)) {
i64 val = page(G + GOT + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
} else {
// Relax GOT-loading ADRP+LDR to an immediate ADRP+ADD
i64 val = page(S + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
u32 reg = bits(*(ul32 *)loc, 4, 0);
*(ul32 *)(loc + 4) = 0x9100'0000 | (reg << 5) | reg; // ADD
*(ul32 *)(loc + 4) |= bits(S + A, 11, 0) << 10;
i++;
}
break;
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC: {
// The ARM64 psABI defines that an `ADRP x0, foo` and `ADD x0, x0,
// :lo12: foo` instruction pair to materialize a PC-relative address
// in a register can be relaxed to `NOP` followed by `ADR x0, foo`
// if foo is in PC ± 1 MiB.
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx) &&
i + 1 < rels.size()) {
i64 val = S + A - P - 4;
const ElfRel<E> &rel2 = rels[i + 1];
if (sign_extend(val, 20) == val &&
rel2.r_type == R_AARCH64_ADD_ABS_LO12_NC &&
rel2.r_sym == rel.r_sym &&
rel2.r_offset == rel.r_offset + 4 &&
rel2.r_addend == rel.r_addend &&
is_adrp(loc) &&
is_add(loc + 4)) {
u32 reg1 = bits(*(ul32 *)loc, 4, 0);
u32 reg2 = bits(*(ul32 *)(loc + 4), 4, 0);
if (reg1 == reg2) {
*(ul32 *)loc = 0xd503'201f; // nop
*(ul32 *)(loc + 4) = 0x1000'0000 | reg1; // adr
write_adr(loc + 4, val);
i++;
break;
}
}
}
i64 val = page(S + A) - page(P);
if (rel.r_type == R_AARCH64_ADR_PREL_PG_HI21)
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
break;
}
case R_AARCH64_ADR_PREL_LO21:
check(S + A - P, -(1LL << 20), 1LL << 20);
write_adr(loc, S + A - P);
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26: {
if (sym.is_remaining_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(ul32 *)loc = 0xd503'201f; // nop
break;
}
i64 val = S + A - P;
if (val < -(1 << 27) || (1 << 27) <= val)
val = get_thunk_addr(i) + A - P;
*(ul32 *)loc |= bits(val, 27, 2);
break;
}
case R_AARCH64_PLT32:
check(S + A - P, -(1LL << 31), 1LL << 31);
*(ul32 *)loc = S + A - P;
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
check(S + A - P, -(1LL << 20), 1LL << 20);
*(ul32 *)loc |= bits(S + A - P, 20, 2) << 5;
break;
case R_AARCH64_PREL16:
check(S + A - P, -(1LL << 15), 1LL << 15);
*(ul16 *)loc = S + A - P;
break;
case R_AARCH64_PREL32:
check(S + A - P, -(1LL << 31), 1LL << 32);
*(ul32 *)loc = S + A - P;
break;
case R_AARCH64_PREL64:
*(ul64 *)loc = S + A - P;
break;
case R_AARCH64_LD64_GOT_LO12_NC:
*(ul32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
break;
case R_AARCH64_LD64_GOTPAGE_LO15: {
i64 val = G + GOT + A - page(GOT);
check(val, 0, 1 << 15);
*(ul32 *)loc |= bits(val, 14, 3) << 10;
break;
}
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
i64 val = page(sym.get_gottp_addr(ctx) + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
break;
}
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
*(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
break;
case R_AARCH64_TLSLE_MOVW_TPREL_G0: {
i64 val = S + A - ctx.tp_addr;
check(val, -(1 << 15), 1 << 15);
write_movn_movz(loc, val);
break;
}
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 15, 0) << 5;
break;
case R_AARCH64_TLSLE_MOVW_TPREL_G1: {
i64 val = S + A - ctx.tp_addr;
check(val, -(1LL << 31), 1LL << 31);
write_movn_movz(loc, val >> 16);
break;
}
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 31, 16) << 5;
break;
case R_AARCH64_TLSLE_MOVW_TPREL_G2: {
i64 val = S + A - ctx.tp_addr;
check(val, -(1LL << 47), 1LL << 47);
write_movn_movz(loc, val >> 32);
break;
}
case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
i64 val = S + A - ctx.tp_addr;
check(val, 0, 1LL << 24);
*(ul32 *)loc |= bits(val, 23, 12) << 10;
break;
}
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
check(S + A - ctx.tp_addr, 0, 1 << 12);
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 11, 0) << 10;
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 11, 0) << 10;
break;
case R_AARCH64_TLSGD_ADR_PAGE21: {
i64 val = page(sym.get_tlsgd_addr(ctx) + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
break;
}
case R_AARCH64_TLSGD_ADD_LO12_NC:
*(ul32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
// ARM64 TLSDESC uses the following code sequence to materialize
// a TP-relative address in x0.
//
// adrp x0, 0
// R_AARCH64_TLSDESC_ADR_PAGE21 foo
// ldr x1, [x0]
// R_AARCH64_TLSDESC_LD64_LO12 foo
// add x0, x0, #0
// R_AARCH64_TLSDESC_ADD_LO12 foo
// blr x1
// R_AARCH64_TLSDESC_CALL foo
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// nop
// nop
// adrp x0, :gottprel:foo
// ldr x0, [x0, :gottprel_lo12:foo]
//
// or to the following for executable.
//
// nop
// nop
// movz x0, :tls_offset_hi:foo, lsl #16
// movk x0, :tls_offset_lo:foo
if (sym.has_tlsdesc(ctx)) {
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
} else {
*(ul32 *)loc = 0xd503'201f; // nop
}
break;
case R_AARCH64_TLSDESC_LD64_LO12:
if (sym.has_tlsdesc(ctx))
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
else
*(ul32 *)loc = 0xd503'201f; // nop
break;
case R_AARCH64_TLSDESC_ADD_LO12:
if (sym.has_tlsdesc(ctx)) {
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0x9000'0000; // adrp x0, 0
write_adrp(loc, page(sym.get_gottp_addr(ctx) + A) - page(P));
} else {
*(ul32 *)loc = 0xd2a0'0000; // movz x0, 0, lsl #16
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 32, 16) << 5;
}
break;
case R_AARCH64_TLSDESC_CALL:
if (sym.has_tlsdesc(ctx)) {
// Do nothing
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0xf940'0000; // ldr x0, [x0, 0]
*(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
} else {
*(ul32 *)loc = 0xf280'0000; // movk x0, 0
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 15, 0) << 5;
}
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_AARCH64_ABS64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
case R_AARCH64_ABS32: {
i64 val = S + A;
check(val, 0, 1LL << 32);
*(ul32 *)loc = val;
break;
}
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_AARCH64_ABS64:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_AARCH64_MOVW_UABS_G3:
scan_absrel(ctx, sym, rel);
break;
case R_AARCH64_ADR_GOT_PAGE:
// An ADR_GOT_PAGE and GOT_LO12_NC relocation pair is used to load a
// symbol's address from GOT. If the GOT value is a link-time
// constant, we may be able to rewrite the ADRP+LDR instruction pair
// with an ADRP+ADD, eliminating a GOT memory load.
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx) &&
i + 1 < rels.size()) {
// ADRP+LDR must be consecutive and use the same register to relax.
const ElfRel<E> &rel2 = rels[i + 1];
if (rel2.r_type == R_AARCH64_LD64_GOT_LO12_NC &&
rel2.r_offset == rel.r_offset + 4 &&
rel2.r_sym == rel.r_sym &&
rel.r_addend == 0 &&
rel2.r_addend == 0 &&
is_adrp(loc) &&
is_ldr(loc + 4)) {
u32 rd = bits(*(ul32 *)loc, 4, 0);
u32 rn = bits(*(ul32 *)(loc + 4), 9, 5);
u32 rt = bits(*(ul32 *)(loc + 4), 4, 0);
if (rd == rn && rn == rt) {
i++;
break;
}
}
}
sym.flags |= NEEDS_GOT;
break;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_LD64_GOTPAGE_LO15:
sym.flags |= NEEDS_GOT;
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
case R_AARCH64_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
sym.flags |= NEEDS_GOTTP;
break;
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC:
scan_pcrel(ctx, sym, rel);
break;
case R_AARCH64_TLSGD_ADR_PAGE21:
sym.flags |= NEEDS_TLSGD;
break;
case R_AARCH64_TLSDESC_CALL:
scan_tlsdesc(ctx, sym);
break;
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
check_tlsle(ctx, sym, rel);
break;
case R_AARCH64_ADD_ABS_LO12_NC:
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_MOVW_UABS_G0:
case R_AARCH64_MOVW_UABS_G0_NC:
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_TLSGD_ADD_LO12_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSDESC_ADR_PAGE21:
case R_AARCH64_TLSDESC_LD64_LO12:
case R_AARCH64_TLSDESC_ADD_LO12:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void Thunk<E>::copy_buf(Context<E> &ctx) {
static const ul32 insn[] = {
0x9000'0010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x9100'0210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0xd61f'0200, // br x16
0xd503'201f, // nop
};
static_assert(E::thunk_size == sizeof(insn));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
for (Symbol<E> *sym : symbols) {
u64 S = sym->get_addr(ctx);
memcpy(buf, insn, E::thunk_size);
write_adrp(buf, page(S) - page(P));
*(ul32 *)(buf + 4) |= bits(S, 11, 0) << 10;
buf += E::thunk_size;
P += E::thunk_size;
}
}
} // namespace mold::elf
-615
View File
@@ -1,615 +0,0 @@
// i386 is similar to x86-64 but lacks PC-relative memory access
// instructions. So it's not straightforward to support position-
// independent code (PIC) on that target.
//
// If an object file is compiled with -fPIC, a function that needs to load
// a value from memory first obtains its own address with the following
// code
//
// call __x86.get_pc_thunk.bx
//
// where __x86.get_pc_thunk.bx is defined as
//
// __x86.get_pc_thunk.bx:
// mov (%esp), %ebx # move the return address to %ebx
// ret
//
// . With the function's own address (or, more precisely, the address
// immediately after the call instruction), the function can compute an
// absolute address of a variable with its address + link-time constant.
//
// Executing call-mov-ret isn't very cheap, and allocating one register to
// store PC isn't cheap too, especially given that i386 has only 8
// general-purpose registers. But that's the cost of PIC on i386. You need
// to pay it when creating a .so and a position-independent executable.
//
// When a position-independent function calls another function, it sets
// %ebx to the address of .got. Position-independent PLT entries use that
// register to load values from .got.plt/.got.
//
// If we are creating a position-dependent executable (PDE), we can't
// assume that %ebx is set to .got. For PDE, we need to create position-
// dependent PLT entries which don't use %ebx.
//
// https://github.com/rui314/psabi/blob/main/i386.pdf
#include "mold.h"
namespace mold::elf {
using E = I386;
template <>
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_386_8:
case R_386_PC8:
return *loc;
case R_386_16:
case R_386_PC16:
return *(ul16 *)loc;
case R_386_32:
case R_386_PC32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_TLS_LDM:
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
case R_386_TLS_GD:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_GOTDESC:
return *(ul32 *)loc;
default:
return 0;
}
}
template <>
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_386_NONE:
break;
case R_386_8:
case R_386_PC8:
*loc = val;
break;
case R_386_16:
case R_386_PC16:
*(ul16 *)loc = val;
break;
case R_386_32:
case R_386_PC32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_TLS_LDM:
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
case R_386_TLS_GD:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_GOTDESC:
*(ul32 *)loc = val;
break;
default:
unreachable();
}
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0x51, // push %ecx
0x8d, 0x8b, 0, 0, 0, 0, // lea GOTPLT+4(%ebx), %ecx
0xff, 0x31, // push (%ecx)
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 7) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
} else {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0x51, // push %ecx
0xb9, 0, 0, 0, 0, // mov GOTPLT+4, %ecx
0xff, 0x31, // push (%ecx)
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = ctx.gotplt->shdr.sh_addr + 4;
}
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx);
}
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx);
}
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_386_32:
*(ul32 *)loc = val;
break;
case R_386_PC32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
static u32 relax_got32x(u8 *loc) {
// mov imm(%reg1), %reg2 -> lea imm(%reg1), %reg2
if (loc[0] == 0x8b)
return 0x8d00 | loc[1];
return 0;
}
// Relax GD to LE
static void relax_gd_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %eax
};
switch (rel.r_type) {
case R_386_PLT32:
case R_386_PC32:
memcpy(loc - 3, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
case R_386_GOT32:
case R_386_GOT32X:
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 6) = val;
break;
default:
unreachable();
}
}
// Relax LD to LE
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
switch (rel.r_type) {
case R_386_PLT32:
case R_386_PC32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
}
case R_386_GOT32:
case R_386_GOT32X: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
0x90, // nop
};
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
}
default:
unreachable();
}
}
static u32 relax_tlsdesc_to_ie(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0x8d83: return 0x8b83; // lea 0(%ebx), %eax -> mov 0(%ebx), %eax
case 0x8d9b: return 0x8b9b; // lea 0(%ebx), %ebx -> mov 0(%ebx), %ebx
case 0x8d8b: return 0x8b8b; // lea 0(%ebx), %ecx -> mov 0(%ebx), %ecx
case 0x8d93: return 0x8b93; // lea 0(%ebx), %edx -> mov 0(%ebx), %edx
case 0x8db3: return 0x8bb3; // lea 0(%ebx), %esi -> mov 0(%ebx), %esi
case 0x8dbb: return 0x8bbb; // lea 0(%ebx), %edi -> mov 0(%ebx), %edi
case 0x8da3: return 0x8ba3; // lea 0(%ebx), %esp -> mov 0(%ebx), %esp
case 0x8dab: return 0x8bab; // lea 0(%ebx), %ebp -> mov 0(%ebx), %ebp
}
return 0;
}
static u32 relax_tlsdesc_to_le(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0x8d83: return 0x90b8; // lea 0(%ebx), %eax -> mov $0, %eax
case 0x8d9b: return 0x90bb; // lea 0(%ebx), %ebx -> mov $0, %ebx
case 0x8d8b: return 0x90b9; // lea 0(%ebx), %ecx -> mov $0, %ecx
case 0x8d93: return 0x90ba; // lea 0(%ebx), %edx -> mov $0, %edx
case 0x8db3: return 0x90be; // lea 0(%ebx), %esi -> mov $0, %esi
case 0x8dbb: return 0x90bf; // lea 0(%ebx), %edi -> mov $0, %edi
case 0x8da3: return 0x90bc; // lea 0(%ebx), %esp -> mov $0, %esp
case 0x8dab: return 0x90bd; // lea 0(%ebx), %ebp -> mov $0, %ebp
}
return 0;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
u64 S = sym.get_addr(ctx);
u64 A = get_addend(*this, rel);
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_386_8:
check(S + A, 0, 1 << 8);
*loc = S + A;
break;
case R_386_16:
check(S + A, 0, 1 << 16);
*(ul16 *)loc = S + A;
break;
case R_386_32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_386_PC8:
check(S + A - P, -(1 << 7), 1 << 7);
*loc = S + A - P;
break;
case R_386_PC16:
check(S + A - P, -(1 << 15), 1 << 15);
*(ul16 *)loc = S + A - P;
break;
case R_386_PC32:
case R_386_PLT32:
*(ul32 *)loc = S + A - P;
break;
case R_386_GOT32:
*(ul32 *)loc = G + A;
break;
case R_386_GOT32X:
if (sym.has_got(ctx)) {
*(ul32 *)loc = G + A;
} else {
u32 insn = relax_got32x(loc - 2);
assert(insn);
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = S + A - GOT;
}
break;
case R_386_GOTOFF:
*(ul32 *)loc = S + A - GOT;
break;
case R_386_GOTPC:
*(ul32 *)loc = GOT + A - P;
break;
case R_386_TLS_GOTIE:
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
break;
case R_386_TLS_LE:
*(ul32 *)loc = S + A - ctx.tp_addr;
break;
case R_386_TLS_IE:
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A;
break;
case R_386_TLS_GD:
if (sym.has_tlsgd(ctx))
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
else
relax_gd_to_le(loc, rels[++i], S - ctx.tp_addr);
break;
case R_386_TLS_LDM:
if (ctx.got->has_tlsld(ctx))
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
else
relax_ld_to_le(loc, rels[++i], ctx.tp_addr - ctx.tls_begin);
break;
case R_386_TLS_LDO_32:
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_386_SIZE32:
*(ul32 *)loc = sym.esym().st_size + A;
break;
case R_386_TLS_GOTDESC:
// i386 TLSDESC uses the following code sequence to materialize
// a TP-relative address in %eax.
//
// lea 0(%ebx), %eax
// R_386_TLS_GOTDESC foo
// call *(%eax)
// R_386_TLS_DESC_CALL foo
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// mov foo@GOTTPOFF(%ebx), %eax
// nop
//
// or to the following for executable.
//
// mov $foo@TPOFF, %eax
// nop
//
// We allow the following alternative code sequence too because
// LLVM emits such code.
//
// lea 0(%ebx), %reg
// R_386_TLS_GOTDESC foo
// mov %reg, %eax
// call *(%eax)
// R_386_TLS_DESC_CALL foo
if (sym.has_tlsdesc(ctx)) {
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
} else if (sym.has_gottp(ctx)) {
u32 insn = relax_tlsdesc_to_ie(loc - 2);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
} else {
u32 insn = relax_tlsdesc_to_le(loc - 2);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = S + A - ctx.tp_addr;
}
break;
case R_386_TLS_DESC_CALL:
if (!sym.has_tlsdesc(ctx)) {
// call *(%eax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : get_addend(*this, rel);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_386_8:
check(S + A, 0, 1 << 8);
*loc = S + A;
break;
case R_386_16:
check(S + A, 0, 1 << 16);
*(ul16 *)loc = S + A;
break;
case R_386_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
case R_386_PC8:
check(S + A, -(1 << 7), 1 << 7);
*loc = S + A;
break;
case R_386_PC16:
check(S + A, -(1 << 15), 1 << 15);
*(ul16 *)loc = S + A;
break;
case R_386_PC32:
*(ul32 *)loc = S + A;
break;
case R_386_GOTPC:
*(ul32 *)loc = GOT + A;
break;
case R_386_GOTOFF:
*(ul32 *)loc = S + A - GOT;
break;
case R_386_TLS_LDO_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_386_SIZE32:
*(ul32 *)loc = sym.esym().st_size + A;
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
if (rel.r_type == R_386_TLS_GD || rel.r_type == R_386_TLS_LDM) {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": " << rel << " must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_PC32 &&
ty != R_386_GOT32 && ty != R_386_GOT32X)
Fatal(ctx) << *this << ": " << rel << " must be followed by PLT or GOT32";
}
switch (rel.r_type) {
case R_386_8:
case R_386_16:
scan_absrel(ctx, sym, rel);
break;
case R_386_32:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_386_PC8:
case R_386_PC16:
case R_386_PC32:
scan_pcrel(ctx, sym, rel);
break;
case R_386_GOT32:
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_GOT32X:
// We always want to relax GOT32X even if --no-relax is given
// because static PIE doesn't work without it.
if (sym.is_pcrel_linktime_const(ctx) && relax_got32x(loc - 2)) {
// Do nothing
} else {
sym.flags |= NEEDS_GOT;
}
break;
case R_386_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_386_TLS_GOTIE:
case R_386_TLS_IE:
sym.flags |= NEEDS_GOTTP;
break;
case R_386_TLS_GD:
// We always relax if -static because libc.a doesn't contain
// __tls_get_addr().
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) ||
ctx.arg.is_static)
i++;
else
sym.flags |= NEEDS_TLSGD;
break;
case R_386_TLS_LDM:
// We always relax if -static because libc.a doesn't contain
// __tls_get_addr().
if (ctx.arg.is_static || (ctx.arg.relax && !ctx.arg.shared))
i++;
else
ctx.needs_tlsld = true;
break;
case R_386_TLS_GOTDESC:
scan_tlsdesc(ctx, sym);
break;
case R_386_TLS_LE:
check_tlsle(ctx, sym, rel);
break;
case R_386_GOTOFF:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_DESC_CALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
-681
View File
@@ -1,681 +0,0 @@
// LoongArch is a new RISC ISA announced in 2021 by Loongson. The ISA
// feels like a modernized MIPS with a hint of RISC-V flavor, although
// it's not compatible with either one.
//
// While LoongArch is a fresh and clean ISA, its technological advantage
// over other modern RISC ISAs such as RISC-V doesn't seem to be very
// significant. It appears that the real selling point of LoongArch is
// that the ISA is developed and controlled by a Chinese company,
// reflecting a desire for domestic CPUs. Loongson is actively working on
// bootstrapping the entire ecosystem for LoongArch, sending patches to
// Linux, GCC, LLVM, etc.
//
// All instructions are 4 bytes long in LoongArch and aligned to 4-byte
// boundaries. It has 32 general-purpose registers. Among these, $t0 - $t8
// (aliases for $r12 - $r20) are temporary registers that we can use in
// our PLT and range extension thunks.
//
// The psABI defines a few linker relaxations. We haven't supported them
// yet.
//
// https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
#if MOLD_LOONGARCH64 || MOLD_LOONGARCH32
#include "mold.h"
namespace mold::elf {
using E = MOLD_TARGET;
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 hi20(u64 val, u64 pc) {
// A PC-relative address with a 32 bit offset is materialized in a
// register with the following instructions:
//
// pcalau12i $rN, %pc_hi20(sym)
// addi.d $rN, $rN, %lo12(sym)
//
// PCALAU12I materializes bits [63:12] by computing (pc + imm << 12)
// and zero-clear [11:0]. ADDI.D sign-extends its 12 bit immediate and
// add it to the register. To compensate the sign-extension, PCALAU12I
// needs to materialize a 0x1000 larger value than the desired [63:12]
// if [11:0] is sign-extended.
//
// This is similar but different from RISC-V because RISC-V's AUIPC
// doesn't zero-clear [11:0].
return bits(page(val + 0x800) - page(pc), 31, 12);
}
static u64 hi64(u64 val, u64 pc) {
// A PC-relative 64-bit address is materialized with the following
// instructions for the large code model:
//
// pcalau12i $rN, %pc_hi20(sym)
// addi.d $rM, $zero, %lo12(sym)
// lu32i.d $rM, %pc64_lo20(sym)
// lu52i.d $rM, $r12, %pc64_hi12(sym)
// add.d $rN, $rN, $rM
//
// PCALAU12I computes (pc + imm << 12) to materialize a 64-bit value.
// ADDI.D adds a sign-extended 12 bit value to a register. LU32I.D and
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
//
// Compensating all the sign-extensions is a bit complicated.
u64 x = page(val) - page(pc);
if (val & 0x800)
x += 0x1000 - 0x1'0000'0000;
if (x & 0x8000'0000)
x += 0x1'0000'0000;
return x;
}
static u64 higher20(u64 val, u64 pc) {
return bits(hi64(val, pc), 51, 32);
}
static u64 highest12(u64 val, u64 pc) {
return bits(hi64(val, pc), 63, 52);
}
static void write_k12(u8 *loc, u32 val) {
// opcode, [11:0], rj, rd
*(ul32 *)loc &= 0b1111111111'000000000000'11111'11111;
*(ul32 *)loc |= bits(val, 11, 0) << 10;
}
static void write_k16(u8 *loc, u32 val) {
// opcode, [15:0], rj, rd
*(ul32 *)loc &= 0b111111'0000000000000000'11111'11111;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
}
static void write_j20(u8 *loc, u32 val) {
// opcode, [19:0], rd
*(ul32 *)loc &= 0b1111111'00000000000000000000'11111;
*(ul32 *)loc |= bits(val, 19, 0) << 5;
}
static void write_d5k16(u8 *loc, u32 val) {
// opcode, [15:0], rj, [20:16]
*(ul32 *)loc &= 0b111111'0000000000000000'11111'00000;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
*(ul32 *)loc |= bits(val, 20, 16);
}
static void write_d10k16(u8 *loc, u32 val) {
// opcode, [15:0], [25:16]
*(ul32 *)loc &= 0b111111'0000000000000000'0000000000;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
*(ul32 *)loc |= bits(val, 25, 16);
}
template <>
void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
static const ul32 insn_64[] = {
0x1a00'000e, // pcalau12i $t2, %pc_hi20(.got.plt)
0x0011'bdad, // sub.d $t1, $t1, $t3
0x28c0'01cf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
0x02ff'51ad, // addi.d $t1, $t1, -44 # .plt entry
0x02c0'01cc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
0x0045'05ad, // srli.d $t1, $t1, 1 # .plt entry offset
0x28c0'218c, // ld.d $t0, $t0, 8 # link map
0x4c00'01e0, // jr $t3
};
static const ul32 insn_32[] = {
0x1a00'000e, // pcalau12i $t2, %pc_hi20(.got.plt)
0x0011'3dad, // sub.w $t1, $t1, $t3
0x2880'01cf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
0x02bf'51ad, // addi.w $t1, $t1, -44 # .plt entry
0x0280'01cc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
0x0044'89ad, // srli.w $t1, $t1, 2 # .plt entry offset
0x2880'118c, // ld.w $t0, $t0, 4 # link map
0x4c00'01e0, // jr $t3
};
u64 gotplt = ctx.gotplt->shdr.sh_addr;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, E::is_64 ? insn_64 : insn_32, E::plt_hdr_size);
write_j20(buf, hi20(gotplt, plt));
write_k12(buf + 8, gotplt);
write_k12(buf + 16, gotplt);
}
static const ul32 plt_entry_64[] = {
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
0x28c0'01ef, // ld.d $t3, $t3, %lo12([email protected])
0x4c00'01ed, // jirl $t1, $t3, 0
0x0340'0000, // nop
};
static const ul32 plt_entry_32[] = {
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
0x2880'01ef, // ld.w $t3, $t3, %lo12([email protected])
0x4c00'01ed, // jirl $t1, $t3, 0
0x0340'0000, // nop
};
template <>
void write_plt_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
write_j20(buf, hi20(gotplt, plt));
write_k12(buf + 4, gotplt);
}
template <>
void write_pltgot_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 got = sym.get_got_pltgot_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
write_j20(buf, hi20(got, plt));
write_k12(buf + 4, got);
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + val) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += val;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += val;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += val;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += val;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= val;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= val;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= val;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= val;
break;
case R_LARCH_32_PCREL:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_LARCH_64_PCREL:
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || rel.r_type == R_LARCH_RELAX ||
rel.r_type == R_LARCH_MARK_LA || rel.r_type == R_LARCH_MARK_PCREL ||
rel.r_type == R_LARCH_ALIGN)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto check_branch = [&](i64 val, i64 lo, i64 hi) {
if (val & 0b11)
Error(ctx) << *this << ": misaligned symbol " << sym
<< " for relocation " << rel;
check(val, lo, hi);
};
// Unlike other psABIs, the LoongArch ABI uses the same relocation
// types to refer to GOT entries for thread-local symbols and regular
// ones. Therefore, G may refer to a TLSGD or a regular GOT slot
// depending on the symbol type.
//
// Note that as of August 2023, both GCC and Clang treat TLSLD relocs
// as if they were TLSGD relocs for LoongArch, which is a clear bug.
// We need to handle TLSLD relocs as synonyms for TLSGD relocs for the
// sake of bug compatibility.
auto get_got_idx = [&] {
if (sym.has_tlsgd(ctx))
return sym.get_tlsgd_idx(ctx);
return sym.get_got_idx(ctx);
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = get_got_idx() * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_LARCH_32:
if constexpr (E::is_64)
*(ul32 *)loc = S + A;
else
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_LARCH_64:
assert(E::is_64);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_LARCH_B16:
check_branch(S + A - P, -(1 << 17), 1 << 17);
write_k16(loc, (S + A - P) >> 2);
break;
case R_LARCH_B21:
check_branch(S + A - P, -(1 << 22), 1 << 22);
write_d5k16(loc, (S + A - P) >> 2);
break;
case R_LARCH_B26: {
i64 val = S + A - P;
if (val < -(1 << 27) || (1 << 27) <= val)
val = get_thunk_addr(i) + A - P;
write_d10k16(loc, val >> 2);
break;
}
case R_LARCH_ABS_LO12:
write_k12(loc, S + A);
break;
case R_LARCH_ABS_HI20:
write_j20(loc, (S + A) >> 12);
break;
case R_LARCH_ABS64_LO20:
write_j20(loc, (S + A) >> 32);
break;
case R_LARCH_ABS64_HI12:
write_k12(loc, (S + A) >> 52);
break;
case R_LARCH_PCALA_LO12:
// It looks like R_LARCH_PCALA_LO12 is sometimes used for JIRL even
// though the instruction takes a 16 bit immediate rather than 12 bits.
// It is contrary to the psABI document, but GNU ld has special
// code to handle it, so we accept it too.
if ((*(ul32 *)loc & 0xfc00'0000) == 0x4c00'0000)
write_k16(loc, sign_extend(S + A, 11) >> 2);
else
write_k12(loc, S + A);
break;
case R_LARCH_PCALA_HI20:
write_j20(loc, hi20(S + A, P));
break;
case R_LARCH_PCALA64_LO20:
write_j20(loc, higher20(S + A, P));
break;
case R_LARCH_PCALA64_HI12:
write_k12(loc, highest12(S + A, P));
break;
case R_LARCH_GOT_PC_LO12:
write_k12(loc, GOT + G + A);
break;
case R_LARCH_GOT_PC_HI20:
write_j20(loc, hi20(GOT + G + A, P));
break;
case R_LARCH_GOT64_PC_LO20:
write_j20(loc, higher20(GOT + G + A, P));
break;
case R_LARCH_GOT64_PC_HI12:
write_k12(loc, highest12(GOT + G + A, P));
break;
case R_LARCH_GOT_LO12:
write_k12(loc, GOT + G + A);
break;
case R_LARCH_GOT_HI20:
write_j20(loc, (GOT + G + A) >> 12);
break;
case R_LARCH_GOT64_LO20:
write_j20(loc, (GOT + G + A) >> 32);
break;
case R_LARCH_GOT64_HI12:
write_k12(loc, (GOT + G + A) >> 52);
break;
case R_LARCH_TLS_LE_LO12:
write_k12(loc, S + A - ctx.tp_addr);
break;
case R_LARCH_TLS_LE_HI20:
write_j20(loc, (S + A - ctx.tp_addr) >> 12);
break;
case R_LARCH_TLS_LE64_LO20:
write_j20(loc, (S + A - ctx.tp_addr) >> 32);
break;
case R_LARCH_TLS_LE64_HI12:
write_k12(loc, (S + A - ctx.tp_addr) >> 52);
break;
case R_LARCH_TLS_IE_PC_LO12:
write_k12(loc, sym.get_gottp_addr(ctx) + A);
break;
case R_LARCH_TLS_IE_PC_HI20:
write_j20(loc, hi20(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE64_PC_LO20:
write_j20(loc, higher20(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE64_PC_HI12:
write_k12(loc, highest12(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE_LO12:
write_k12(loc, sym.get_gottp_addr(ctx) + A);
break;
case R_LARCH_TLS_IE_HI20:
write_j20(loc, (sym.get_gottp_addr(ctx) + A) >> 12);
break;
case R_LARCH_TLS_IE64_LO20:
write_j20(loc, (sym.get_gottp_addr(ctx) + A) >> 32);
break;
case R_LARCH_TLS_IE64_HI12:
write_k12(loc, (sym.get_gottp_addr(ctx) + A) >> 52);
break;
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
check(sym.get_tlsgd_addr(ctx) + A - P, -(1LL << 31), 1LL << 31);
write_j20(loc, hi20(sym.get_tlsgd_addr(ctx) + A, P));
break;
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_HI20:
write_j20(loc, (sym.get_tlsgd_addr(ctx) + A) >> 12);
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + S + A) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += S + A;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += S + A;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += S + A;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += S + A;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= S + A;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= S + A;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= S + A;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= S + A;
break;
case R_LARCH_32_PCREL:
*(ul32 *)loc = S + A - P;
break;
case R_LARCH_64_PCREL:
*(ul64 *)loc = S + A - P;
break;
case R_LARCH_ADD_ULEB128:
overwrite_uleb(loc, read_uleb(loc) + S + A);
break;
case R_LARCH_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
record_undef_error(ctx, rel);
continue;
}
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_LARCH_32:
*(ul32 *)loc = S + A;
break;
case R_LARCH_64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + S + A) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += S + A;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += S + A;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += S + A;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += S + A;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= S + A;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= S + A;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= S + A;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= S + A;
break;
case R_LARCH_TLS_DTPREL32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_LARCH_TLS_DTPREL64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A - ctx.dtp_addr;
break;
case R_LARCH_ADD_ULEB128:
overwrite_uleb(loc, read_uleb(loc) + S + A);
break;
case R_LARCH_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || rel.r_type == R_LARCH_RELAX ||
rel.r_type == R_LARCH_MARK_LA || rel.r_type == R_LARCH_MARK_PCREL ||
rel.r_type == R_LARCH_ALIGN)
continue;
if (record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_LARCH_32:
if constexpr (E::is_64)
scan_absrel(ctx, sym, rel);
else
scan_dyn_absrel(ctx, sym, rel);
break;
case R_LARCH_64:
assert(E::is_64);
scan_dyn_absrel(ctx, sym, rel);
break;
case R_LARCH_B26:
case R_LARCH_PCALA_HI20:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_LARCH_GOT_HI20:
case R_LARCH_GOT_PC_HI20:
sym.flags |= NEEDS_GOT;
break;
case R_LARCH_TLS_IE_HI20:
case R_LARCH_TLS_IE_PC_HI20:
sym.flags |= NEEDS_GOTTP;
break;
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
scan_pcrel(ctx, sym, rel);
break;
case R_LARCH_TLS_LE_HI20:
case R_LARCH_TLS_LE_LO12:
case R_LARCH_TLS_LE64_LO20:
case R_LARCH_TLS_LE64_HI12:
check_tlsle(ctx, sym, rel);
break;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_ABS_HI20:
case R_LARCH_ABS_LO12:
case R_LARCH_ABS64_LO20:
case R_LARCH_ABS64_HI12:
case R_LARCH_PCALA_LO12:
case R_LARCH_PCALA64_LO20:
case R_LARCH_PCALA64_HI12:
case R_LARCH_GOT_PC_LO12:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_GOT_LO12:
case R_LARCH_GOT64_LO20:
case R_LARCH_GOT64_HI12:
case R_LARCH_TLS_IE_PC_LO12:
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
case R_LARCH_TLS_IE_LO12:
case R_LARCH_TLS_IE64_LO20:
case R_LARCH_TLS_IE64_HI12:
case R_LARCH_ADD6:
case R_LARCH_SUB6:
case R_LARCH_ADD8:
case R_LARCH_SUB8:
case R_LARCH_ADD16:
case R_LARCH_SUB16:
case R_LARCH_ADD32:
case R_LARCH_SUB32:
case R_LARCH_ADD64:
case R_LARCH_SUB64:
case R_LARCH_ADD_ULEB128:
case R_LARCH_SUB_ULEB128:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void Thunk<E>::copy_buf(Context<E> &ctx) {
static const ul32 insn[] = {
0x1e00'000c, // pcaddu18i $t0, 0
0x4c00'0180, // jirl $zero, $t0, 0
};
static_assert(E::thunk_size == sizeof(insn));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
for (Symbol<E> *sym : symbols) {
u64 S = sym->get_addr(ctx);
memcpy(buf, insn, sizeof(insn));
write_j20(buf, (S - P + 0x20000) >> 18);
write_k16(buf + 4, (S - P) >> 2);
buf += sizeof(insn);
P += sizeof(insn);
}
}
} // namespace mold::elf
#endif
Loaded 100 of 3330 files, more files were not shown because too many files have changed in this diff. Show more