ELF Linker Improvements in Zig
ELF Linker Improvements in Zig
May 30, 2026 | ELF Linker Improvements | Author: Matthew Lugg
I’ve spent the past few weeks working on our new ELF linker which debuted in Zig 0.16.0. At the time of the 0.16.0 release, this linker implementation was in its fairly early stages, and only really supported linking Zig-only code without any external libraries (even libc)—hence why it was (and still is) disabled by default (it can be enabled with -fnew-linker). However, quite a lot of progress has been made since that initial release!
过去几周,我一直在致力于改进我们在 Zig 0.16.0 中首次推出的全新 ELF 链接器。在 0.16.0 发布时,该链接器的实现仍处于早期阶段,实际上仅支持链接不包含任何外部库(甚至不含 libc)的纯 Zig 代码——这就是为什么它默认处于禁用状态(目前依然如此,但可以通过 -fnew-linker 启用)。不过,自首次发布以来,我们已经取得了相当大的进展!
Here’s a nice milestone—as of my latest PR, the new ELF linker is capable of building the self-hosted Zig compiler with LLVM and LLD libraries enabled, a task which requires quite a few features under the hood.
这是一个重要的里程碑:在我最新的 PR 中,新的 ELF 链接器已经能够构建启用了 LLVM 和 LLD 库的自托管 Zig 编译器,这项任务在底层需要相当多的功能支持。
[mlugg@nebula master]$ # Build the Zig compiler using the new linker:
[mlugg@nebula master]$ zig build -Dno-lib -Dnew-linker -Denable-llvm
[mlugg@nebula master]$ # Use that compiler to build something with LLVM and LLD:
[mlugg@nebula master]$ ./zig-out/bin/zig build-exe ~/hello.zig -fllvm -flld
[mlugg@nebula master]$ ./hello
Hello, World!
Of course, an ELF linker isn’t necessarily the most exciting thing in the world, which is why the headline feature of this new linker is its support for fast incremental compilation. After the recent enhancements, it is now possible (on x86_64 Linux) to perform incremental rebuilds while linking external libraries, C sources, etc—without any additional performance overhead! Here’s a clip of me trying it out on Andrew’s Tetris clone: A few silly changes to Andrew’s Tetris clone being built in around 30ms each.
当然,ELF 链接器本身可能不是世界上最令人兴奋的东西,这就是为什么这个新链接器的核心亮点在于它对快速增量编译的支持。经过最近的增强,现在(在 x86_64 Linux 上)可以在链接外部库、C 源码等时执行增量重构,且不会产生额外的性能开销!以下是我在 Andrew 的俄罗斯方块克隆版上进行测试的片段:对该程序进行一些简单的修改后,每次构建仅需约 30 毫秒。
Oh, and fast incremental rebuilds also work nicely on the Zig compiler itself:
哦,顺便提一下,快速增量重构在 Zig 编译器本身上也表现得非常出色:
[mlugg@nebula master]$ zig build -Dno-lib -Denable-llvm -fincremental --watch
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 36s
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 244ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 228ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 288ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 283ms
The biggest missing feature of this linker implementation right now is that it still does not yet support generating DWARF debug information for Zig code—that’s definitely my next priority. But even without that support, it’s amazing just how useful instant rebuilds can be, for example in any situation where you’re doing a lot of print debugging.
目前该链接器实现中最大的缺失功能是尚不支持为 Zig 代码生成 DWARF 调试信息——这绝对是我接下来的首要任务。但即便没有该功能,即时重构的实用性也令人惊叹,例如在进行大量打印调试(print debugging)的场景中。
If you’re using the master branch of Zig and you’re on x86_64 Linux, consider trying out incremental compilation with the new ELF linker if it previously wasn’t working with your project! I expect many codebases to already work great with it, unlocking the ability to rebuild your project in milliseconds. Of course, if you come across any bugs, please do open an issue.
如果你正在使用 Zig 的 master 分支且运行在 x86_64 Linux 上,如果之前你的项目无法使用该功能,不妨尝试一下新的 ELF 链接器增量编译!我预计许多代码库已经可以很好地兼容它,从而实现毫秒级的项目重构。当然,如果你遇到任何 Bug,请务必提交 Issue。
And if you’re currently sticking to tagged releases of Zig, don’t worry—as Andrew mentioned in his last devlog, Zig 0.17.0 is just around the corner, so it won’t be long before you can try this too!
如果你目前仍在使用 Zig 的稳定版本(tagged releases),也不必担心——正如 Andrew 在上一篇开发日志中所提到的,Zig 0.17.0 即将发布,很快你也能体验到这些新特性了!
May 26, 2026 | Build System Reworked | Author: Andrew Kelley
Big branch just landed: separate the maker process from the configurer process. This devlog entry is essentially a preview of the upcoming release notes, but serves as an advanced notice to those who want to help test out the new features and provide feedback that will guide the Zig project moving forward.
重大分支已合并:将“构建者(maker)”进程与“配置者(configurer)”进程分离。这篇开发日志本质上是即将发布的发行说明的预览,同时也为那些希望帮助测试新功能并提供反馈以指导 Zig 项目未来发展的人们提供预告。
Before, build.zig files plus the build system implementation were all compiled into one bloated process, in Debug mode. After build.zig logic finished constructing a build graph in memory, the “build runner” code executed it.
在此之前,build.zig 文件和构建系统实现都被编译成一个臃肿的进程,并以 Debug 模式运行。在 build.zig 逻辑在内存中构建完构建图(build graph)后,“构建运行器(build runner)”代码会执行它。
Now, build.zig files are compiled into a small process (the “configurer”) in debug mode. After this logic finishes constructing a build graph in memory, it is serialized to a binary configuration file. The parent zig build process is aware of this file and caches it for next time. While waiting for all that, it asynchronously compiles the build graph execution process (the “maker”) in release mode. Once the configuration file is available and the maker process is finished compiling, the maker process is executed, passing it the configuration file. The maker process only needs to be compiled once per zig version thanks to the global cache. The maker process then executes the build graph, which is contained within the serialized configuration file.
现在,build.zig 文件被编译成一个以 Debug 模式运行的小型进程(即“配置者”)。当该逻辑在内存中完成构建图的构造后,它会被序列化为一个二进制配置文件。父级 zig build 进程会识别该文件并将其缓存以供下次使用。在等待此过程的同时,它会异步地以 Release 模式编译构建图执行进程(即“构建者”)。一旦配置文件就绪且“构建者”进程编译完成,“构建者”进程就会被执行,并将配置文件传递给它。得益于全局缓存,“构建者”进程每个 Zig 版本只需编译一次。随后,“构建者”进程会执行包含在序列化配置文件中的构建图。
The primary motivation of this change was to make zig build faster, in three ways:
此项更改的主要动机是为了通过以下三种方式提升 zig build 的速度:
-
Only the user’s build.zig logic will be compiled with each change, rather than the entire build system along with it. This is starting to become more valuable now that we have introduced
--watch,--fuzzand--webui. The build system can grow more features without makingzig buildtake longer. -
Now the build system can skip rerunning the build.zig logic entirely when it knows nothing will change, for example if you add
-freference-traceto your zig build command line, it now avoids re-running your build.zig logic redundantly, using the same configuration as last time. -
Now the process that actually executes the build graph is compiled with optimizations enabled.
-
每次修改时,仅编译用户的
build.zig逻辑,而不是连同整个构建系统一起编译。随着我们引入--watch、--fuzz和--webui,这一点变得越来越重要。构建系统可以在不增加zig build时间的前提下增加更多功能。 -
现在,当构建系统知道没有任何变化时,可以完全跳过重新运行
build.zig逻辑。例如,如果你在zig build命令行中添加-freference-trace,它现在会使用与上次相同的配置,避免冗余地重新运行build.zig逻辑。 -
现在,实际执行构建图的进程在编译时启用了优化。
To demonstrate points 2 and 3, here is the difference between running zig build --help before and after:
为了演示第 2 点和第 3 点,以下是运行 zig build --help 前后的对比:
(Benchmark data omitted for brevity)