DevNews

Mold 2.42 Adds SFrame v3 and Android Packed Relocations

On this page
  1. Mold learns to read SFrame
  2. Smaller binaries, two ways
  3. The compression level was hiding a default
  4. The bugs worth reading about
  5. What we would do
  6. Sources and further reading

Mold 2.42 was tagged in the early hours of Wednesday, August 12, ending the quietest stretch the high speed linker has had in a while: version 2.41 shipped back in April. The headline is broad optimisation work that should show up most on large links across many cores, but the release is more interesting than a speed bump. Mold now parses SFrame version 3 stack unwinding sections rather than treating them as opaque bytes, supports the Android packed relocation format, accepts a compression level on debug sections, and fixes three identical code folding bugs that could quietly change how a program behaved at runtime.

The short answer

Mold 2.42 is out, closing the longest gap between releases the linker has had in some time. The broad theme is optimisation, with the biggest wins expected on large links across many cores. The substance is elsewhere: mold now understands SFrame version 3 stack unwinding sections instead of copying them blindly, supports Android packed relocations, takes a compression level on debug sections, discards compiler internal local symbols by default, and fixes three identical code folding bugs that were quietly capable of changing how a program behaved.

2.42.0tagged August 12, four months after 2.41
SFrame v3the unwinding format mold now parses and rebuilds
3identical code folding bugs that could change runtime behaviour
Answer card: mold 2.42 was released on August 12, 2026, four months after 2.41, adding SFrame version 3 stack unwinding support, Android packed relocation formats APS2 and relr, compression levels on debug sections, the minus w option, default discarding of temporary local symbols, and fixes for three identical code folding correctness bugs.
Mold 2.42 at a glance. Source: the mold 2.42.0 release notes on GitHub, August 12, 2026. PNG

Linker releases are usually easy to summarise: it got faster, here are the numbers. Mold 2.42 does claim to be noticeably faster than its predecessors, particularly when linking large programs on machines with plenty of cores, and after four months of optimisation work that is believable.

We would rather talk about the other half of the release, because the correctness fixes in it are the kind that do not announce themselves.

Mold learns to read SFrame

The most consequential new feature is one most people have not needed yet.

SFrame is a compact stack unwinding format, meant as a lighter alternative to .eh_frame for the asynchronous unwinding that profilers and debuggers do. Until recently it was rare enough that a linker could ignore the details. That changed with binutils 2.46, where the GNU assembler emits SFrame version 3 when given --gsframe. Object files carrying .sframe sections are on their way to being ordinary.

Here is why a linker cannot pass them through untouched. Unwinding data is a table keyed by function address, and the linker is the component that decides which functions survive. Concatenate the input sections blindly and you get entries describing functions that --gc-sections or identical code folding deleted, in an order nobody sorted. A runtime doing a binary search on that table gets wrong answers about a live program's stack.

So mold 2.42 parses input .sframe sections properly: it discards entries for functions that were removed, rebuilds a single merged section sorted by function address, and emits a PT_GNU_SFRAME segment so the runtime can locate the result. Relocatable output with -r is handled too.

Smaller binaries, two ways

Two changes attack output size from different directions.

Android packed relocations arrive as --pack-dyn-relocs=android and --pack-dyn-relocs=android+relr. A position independent binary carries one dynamic relocation entry per address the loader must fix up at load time, and in a large native library that table is not a rounding error. The Android format, APS2, encodes it far more compactly, and the relr variant combines packing with the relative relocation table format. The benefit is concentrated where the loader understands the format, which in practice means native libraries shipped inside Android apps.

The other change is quieter and applies to everyone. Mold now discards temporary local symbols such as .L.str.42 by default, matching lld. Compilers emit these in bulk for unnamed program elements like string literals. Nothing references them through the symbol table, and debug info locates their data by section offset, so removing them loses nothing observable while taking a few percent off large debug builds and keeping compiler internal labels out of symbolized backtraces. --discard-none restores the old behaviour, and -r or --emit-relocs keeps everything regardless.

Terminal figure showing the new mold 2.42 command line options, including pack dyn relocs set to android plus relr, compress debug sections set to zstd level nine, the minus w warning suppression flag, discard none to restore temporary local symbols, and T text segment for GNU ld compatibility.
The new options in mold 2.42. Values are illustrative, the flags and their defaults are from the release notes. PNG

The compression level was hiding a default

--compress-debug-sections now accepts a level, so you can ask for zstd:9 or zlib:6.

The interesting part is what this reveals. Plain zstd and zlib remain accepted and are now documented as equivalent to zstd:3 and zlib:1, which were previously hardcoded defaults nobody had a reason to know. If you generate large debug artifacts and store a lot of them, there is now a knob, and it costs link time to save space. If you do not care, nothing changed.

Two smaller compatibility additions round this out: -w and --no-warnings suppress warnings and cancel --fatal-warnings while still reporting real errors, matching lld, and -Ttext-segment is supported for GNU ld compatibility.

The bugs worth reading about

Identical code folding merges functions that compile to the same machine code, which is a large win on C++ binaries full of template instantiations. It is also a place where being slightly too generous about the word identical turns into a behaviour change rather than a failed link. Mold 2.42 fixes three of these.

The first is longstanding: two functions with identical machine code but different exception tables or personality routines could be folded into one. The instructions match, the exception behaviour does not, and the survivor catches a different set of exceptions than one of the originals did.

The second involves preemptible symbols. A relocation against a preemptible symbol was treated as equivalent to a relocation against a different symbol whose body happened to be identical, which ignores the whole point of preemption: that symbol can be interposed at runtime, and then the two are not the same thing at all.

The third is the most concrete. When ICF folded sections with different alignment requirements, the survivor did not necessarily take the strictest alignment of the group. A function requiring four byte alignment could end up folded into a less aligned copy and placed at an odd address. On x86 that breaks member function pointers, because they use the least significant bit of the address to distinguish virtual from non virtual functions. An odd address makes a non virtual call look virtual.

Alongside these, mold fixed a spurious "refers to a discarded COMDAT section" error when linking a mix of LTO and non LTO archives, a race in COMDAT owner selection during LTO, and several symbol versioning problems, including one where a legacy compatibility alias created with .symver foo, foo@VER could satisfy an unversioned reference and silently bind callers to the legacy implementation instead of the default version.

What we would do

Upgrade, and read two lines of the release notes before you do rather than after.

The correctness fixes are the reason. If you build C++ with ICF enabled, all three folding bugs are live risks in your current linker, and the alignment one in particular fails in a way that will not look like a linker problem when you are debugging it at two in the morning.

The two things that can surprise you are both benign but visible. Local symbol discarding is now on by default, so any tooling that reads temporary local symbols out of the symbol table will find them missing until you pass --discard-none. And if you already pass a bare zstd or zlib to --compress-debug-sections, your compression level is now a documented number rather than an implementation detail.

Sources and further reading

Frequently asked questions

What is SFrame, and why does a linker have to understand it?

SFrame is a compact stack unwinding format, an alternative to the much heavier .eh_frame that profilers and debuggers use to walk a call stack asynchronously. As of binutils 2.46 the GNU assembler emits SFrame version 3 when you pass the --gsframe flag, which means object files carrying .sframe sections are becoming ordinary rather than exotic. A linker cannot treat those sections as opaque bytes to concatenate. If it did, entries would survive for functions that --gc-sections or identical code folding had already removed, and the merged table would not be sorted, so a runtime doing a binary search for an address would get wrong answers. Mold 2.42 therefore parses input .sframe sections, drops entries for functions that no longer exist, rebuilds one merged section sorted by function address, and emits a PT_GNU_SFRAME segment so the runtime can find it.

What are Android packed relocations, and do they matter outside Android?

They are a compression scheme for the dynamic relocation table. A position independent binary carries a relocation entry for every address the dynamic loader must fix up at load time, and in a large library that table can be a substantial fraction of the file. The Android packed format, known as APS2, encodes those entries far more compactly. Mold 2.42 adds --pack-dyn-relocs=android and --pack-dyn-relocs=android+relr, the second combining packing with the relative relocation table format. The name says Android because that is where the format originated and where it is standard, so the direct benefit is to anyone shipping native libraries into an Android app. The mechanism itself is not platform locked, but the loader on the other end has to understand the format, which in practice means Android.

Why is mold now discarding local symbols like .L.str.42 by default?

Because they are compiler internal and nothing needs them. Compilers emit large numbers of temporary local symbols for unnamed program elements such as string literals. Nothing references them through the symbol table, and debug information locates their data by section offset rather than by symbol, so discarding them loses nothing you can observe. What you gain is a few percent off large debug builds and cleaner symbolized addresses, since compiler internal labels stop appearing in backtraces. This matches what lld already does. If you depend on the old behaviour, --discard-none restores it, and relocatable output with -r or --emit-relocs keeps all local symbols regardless.

How serious are the identical code folding fixes?

More serious than most linker bug fixes, because each one could change program behaviour rather than just fail the link. Identical code folding merges functions with identical machine code to shrink the binary, and all three bugs came from the definition of identical being slightly too loose. Two functions with the same instructions but different exception tables or personality routines could be folded together, which changes which exceptions the survivor catches. A relocation against a preemptible symbol was treated as equivalent to one against a different symbol that happened to have the same body, ignoring that a preemptible symbol can be interposed at runtime. And when folding sections with different alignment requirements the survivor did not always take the strictest alignment, so a function needing four byte alignment could land at an odd address, which breaks member function pointers on x86 because they use the low bit to distinguish virtual from non virtual calls.

Should we upgrade, and is anything likely to change under us?

Yes, and the correctness fixes alone justify it if you build C++ with ICF enabled. Two changes are worth knowing about before you upgrade rather than after. Local symbol discarding is now on by default, so if any tooling of yours reads temporary local symbols out of the symbol table it will find them gone, and --discard-none brings them back. And --compress-debug-sections now takes a level, which means the bare zstd and zlib values you may already pass are documented as zstd:3 and zlib:1 rather than being unspecified defaults. If your debug artifacts are large and you store many of them, zstd:9 is now available and costs link time to save space.