1:100644 100644 41bc62cead176f9307835337b00615e2e1f74f6b d21ea2791c62f3b912f586e56882346c00321647 M CMakeLists.txt
2:100644 100644 0466b79bf2dbeb803d653eff2f2a48880da3669b e8d54cc0cd314cc8a7e8e2cf2fb1c20106b33078 M build.zig
3:100644 100755 0a7ba5d277c3ccdc6357f92807b80bb7eb855bc7 0a7ba5d277c3ccdc6357f92807b80bb7eb855bc7 M ci/x86_64-linux-debug-llvm.sh
4:100644 100644 578139c5ac08a4d7eb4e298ea3354e4b9c1ee72d 79a120d5a2ccdde4c037297e677420b065a24b04 M lib/compiler_rt/fma.zig
5:100644 100644 dad60b725bc267a906e5c88abeb1ee342f818160 0f47a0b64781e423ec680cb162eb33e3ec51eddb M lib/std/Build/Step/Compile.zig
6:100644 100644 bef80d782b20e759701c73a3fe85909985415f42 43b542c09e047ff0ae723f710750f396e18bbf08 M lib/std/elf.zig
7:100644 100644 b2116c1a7429b5791d8e39d66dd7e66ad64337e0 55f1fd14cb6cfa8421a142a142802710c9944bd5 M lib/std/zig/system.zig
8:100644 100644 34953ed11a12e334a1ecd995de399421a896a5bb bf7246b8d8b8fe3df2c4ac1c4fb6124fe55e134b M src/Compilation.zig
9:100644 100644 d8751251da9e24db98c79f7964e0ea1a9bde78c8 45f7e509de609a4ab10651d27e49a1cb645eb2b6 M src/Compilation/Config.zig
10:100644 100644 996549774294455b642ef3286d3d20be0bd404f5 f4344af86d46be017beacf31e90c581493517b0f M src/InternPool.zig
11:100644 100644 946e890ea3566e0cc1ab2aae453d389ecfab702b d0576de33d4d3077f7fa177ba4e0b8e9b438ee69 M src/Sema.zig
12:100644 100644 8de562176107ae28fb15ce2e6ed241aa7f176e16 381eacf45ab2389c950aad0dd9f0ed886e9a8bcb M src/Value.zig
13:100644 100644 706bdb51655409a6e2c23e36d580d0c81c01f350 8e9e9c29025c4e82af1d14ece0eca9b7d9bd06e4 M src/Zcu.zig
14:100644 100644 77cddb204e82eeeba3b349b0ed2216974d06f14c 32782e7e8952462d2991ff0b3bf3bbe8e0b72c64 M src/Zcu/PerThread.zig
15:100644 100644 7b13cfc90d68b760251c236bd43890858a088dd9 fe40ba4bbb16ffcbcc2768af9a6f8a57600c1e4f M src/arch/riscv64/CodeGen.zig
16:100644 100644 2e72aff941072c7e5d7164eace99b5d919dc4ece 64a476007cf9881e5101c5c945158252b1ba5b86 M src/arch/riscv64/Emit.zig
17:100644 100644 6fb8e23a98c3b0e89ee5139429fc11626fb2ecc7 204ab6a3c06226635b1619b60c205f02f8447884 M src/arch/riscv64/Mir.zig
18:100644 100644 d41f1cdf07aa54b3c55a9917826a7b55a7da23b0 26282b09ab4f5c831cea32e9c9dbf8d6e0b2256a M src/arch/sparc64/Emit.zig
19:100644 100644 842ac10fed1af8fbaeadb7b2c0404a20f806abc0 41d135eb831fefe51afd3fde7925596fce9d441b M src/arch/sparc64/Mir.zig
20:100644 100644 b5760c95ac2b54f5e03c32e3d61a4c32e01f3a46 1827b50d61a06e05f525461fb8261922f36920bf M src/arch/x86_64/CodeGen.zig
21:100644 100644 84bb325322ac20c100da1e85c715a99f7e251deb c2b38d8e6d45b3439d2f8bb6cf9e518ac17784cc M src/arch/x86_64/Emit.zig
22:100644 100644 e70018850d621aff1a360044014f1ea1e8516a44 caf41ffb392d07b8a455f09181c77ffb8a679d7e M src/arch/x86_64/Mir.zig
23:100644 100644 835093d11ab8d38f622252233101c1a3c9d8904d 05048109335e0256d91c5f350d7fa900ce08e402 M src/arch/x86_64/bits.zig
24:100644 100644 03ff7134f62d04025081f06b50248545433130ec 1d9c67175ef204933a5463393cad189d0e59e7fa M src/arch/x86_64/encoder.zig
25:100644 100644 56e6e7c99fb3bd8208ce92e912c03f3d2404f2c1 4cbf3f56169ec9227f961a72720898cc23df3f96 M src/codegen.zig
26:100644 100644 318a51da95bfe1c047447f96cde8a83f993c9f80 be6478eae896f3d70d874e609d3c3d263946872a M src/codegen/aarch64/Mir.zig
27:100644 100644 ac30cedecd8318da8f38e96e45d763fc2d70bc1f 266796a2dc2dda022bd7c4c3027c950a446d9dc9 M src/dev.zig
28:100644 100644 27cd6620e359284f0a6398aa23a5f8ade66446d7 277013efb3094cee331db13b15fd1e5d2d08d7e4 M src/link.zig
29:100644 100644 e016473531aede3998c41f42a85745fbf017c63c f3e3b3d0b530d498f1b8d4518c1d9c06d4a69baa M src/link/Coff.zig
30:100644 100644 f4af5f4148ad11f2eeb0688ccd11a39120404d6b 595e3583d5d540bdd0fe0ce1727d0a25eed72f50 M src/link/Dwarf.zig
31:100644 100644 b74406c368d5cc4eb3da41365236bc79387bf7c2 d7e3cf62f3a378970682797fb538227d9c70795d M src/link/Elf/LinkerDefined.zig
32:100644 100644 8c79def16bff14a9878ab22cea2ee6e1aca7c160 4dce40e37094e9a48db41d35ce189af38b63c287 M src/link/Elf/SharedObject.zig
33:100644 100644 bf65d04e4a13b3980749bfcc1af24f52d4ac3b49 a3ede11dc4003cd0f9d1f671dfccc5c095b16a82 M src/link/Elf/ZigObject.zig
34:000000 100644 0000000000000000000000000000000000000000 e43ebf2639ad88db8d125d2a79877173d771f73d A src/link/Elf2.zig
35:100644 100644 b1fc6528d5808663942b0428869f0c72efda1931 5a0a71f380c8a83c66b4f4e5391e4ec54469033e M src/link/MachO/ZigObject.zig
36:000000 100644 0000000000000000000000000000000000000000 d04a8d533a03f8934a8546311007b867bde353d0 A src/link/MappedFile.zig
37:100644 100644 9f4535e1fe4ef7c6458ee94b571b4157ba422dc8 742b4664f178955b4668f130ebbacab0b9ac2550 M src/link/Queue.zig
38:100644 100644 a125a70aae790e7b750c96304bae1bdb3c86376c d60b91c30f93695076b8299c7e92d8f734812d32 M src/link/Wasm.zig
39:100644 100644 26d44c4463be802e0e7dea899980b28010b8ffc7 9d43e47ea2dbd949122ab7abfdf285ade21bc987 M src/main.zig
40:100644 100644 e999d2ae22663bcd84a9eb1fc112a317b12a9c02 1cf5c9b08274a55f6cd956ae433e4fb7141e1abd M src/target.zig
41:100644 100644 01605bdeeb36e4d51e986526e94c9a36e2cf07fa e492930031e7fb5d22d68402bc3e59299a084402 M test/incremental/change_exports
42:100644 100644 3e400b854eb60b4a81a3c9b2f0e3511e11ec3b26 699134100eec6942bc2414d29ab7188332f3a45a M test/incremental/change_panic_handler
43:100644 100644 9f7eb6c12f10b42d80e998f352ac9a0a1d5d0842 2d068d593eeb77a07b9c249fc0d7542f1cee4c6c M test/incremental/change_panic_handler_explicit
44:100644 100644 97049a1fc03ae2d9dfbdd07e6c06a9e756f133aa f3bfbbdd6922a0266e42c41f8e92cc96e15bc9fc M test/incremental/change_struct_same_fields
45:100644 100644 5c5d3329750eadc813f892bce6e05234ad563242 3bcae1cd211281cc29d4b970240174c40613fdd6 M test/incremental/type_becomes_comptime_only
46
47diff --git a/CMakeLists.txt b/CMakeLists.txt
48index 41bc62cead176f9307835337b00615e2e1f74f6b..d21ea2791c62f3b912f586e56882346c00321647 100644
49--- a/CMakeLists.txt
50+++ b/CMakeLists.txt
51@@ -583,6 +583,7 @@ set(ZIG_STAGE2_SOURCES
52 src/link/Elf/relocatable.zig
53 src/link/Elf/relocation.zig
54 src/link/Elf/synthetic_sections.zig
55+ src/link/Elf2.zig
56 src/link/Goff.zig
57 src/link/LdScript.zig
58 src/link/Lld.zig
59@@ -612,6 +613,7 @@ set(ZIG_STAGE2_SOURCES
60 src/link/MachO/synthetic.zig
61 src/link/MachO/Thunk.zig
62 src/link/MachO/uuid.zig
63+ src/link/MappedFile.zig
64 src/link/Queue.zig
65 src/link/StringTable.zig
66 src/link/Wasm.zig
67diff --git a/build.zig b/build.zig
68index 0466b79bf2dbeb803d653eff2f2a48880da3669b..e8d54cc0cd314cc8a7e8e2cf2fb1c20106b33078 100644
69--- a/build.zig
70+++ b/build.zig
71@@ -202,6 +202,7 @@ pub fn build(b: *std.Build) !void {
72 });
73 exe.pie = pie;
74 exe.entitlements = entitlements;
75+ exe.use_new_linker = b.option(bool, "new-linker", "Use the new linker");
76
77 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
78 exe.use_llvm = use_llvm;
79diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh
80old mode 100644
81new mode 100755
82diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig
83index 578139c5ac08a4d7eb4e298ea3354e4b9c1ee72d..79a120d5a2ccdde4c037297e677420b065a24b04 100644
84--- a/lib/compiler_rt/fma.zig
85+++ b/lib/compiler_rt/fma.zig
86@@ -203,7 +203,7 @@ fn add_adjusted(a: f64, b: f64) f64 {
87 if (uhii & 1 == 0) {
88 // hibits += copysign(1.0, sum.hi, sum.lo)
89 const uloi: u64 = @bitCast(sum.lo);
90- uhii += 1 - ((uhii ^ uloi) >> 62);
91+ uhii = uhii + 1 - ((uhii ^ uloi) >> 62);
92 sum.hi = @bitCast(uhii);
93 }
94 }
95@@ -217,7 +217,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 {
96 const bits_lost = -@as(i32, @intCast((uhii >> 52) & 0x7FF)) - scale + 1;
97 if ((bits_lost != 1) == (uhii & 1 != 0)) {
98 const uloi: u64 = @bitCast(sum.lo);
99- uhii += 1 - (((uhii ^ uloi) >> 62) & 2);
100+ uhii = uhii + 1 - (((uhii ^ uloi) >> 62) & 2);
101 sum.hi = @bitCast(uhii);
102 }
103 }
104@@ -259,7 +259,7 @@ fn add_adjusted128(a: f128, b: f128) f128 {
105 if (uhii & 1 == 0) {
106 // hibits += copysign(1.0, sum.hi, sum.lo)
107 const uloi: u128 = @bitCast(sum.lo);
108- uhii += 1 - ((uhii ^ uloi) >> 126);
109+ uhii = uhii + 1 - ((uhii ^ uloi) >> 126);
110 sum.hi = @bitCast(uhii);
111 }
112 }
113@@ -284,7 +284,7 @@ fn add_and_denorm128(a: f128, b: f128, scale: i32) f128 {
114 const bits_lost = -@as(i32, @intCast((uhii >> 112) & 0x7FFF)) - scale + 1;
115 if ((bits_lost != 1) == (uhii & 1 != 0)) {
116 const uloi: u128 = @bitCast(sum.lo);
117- uhii += 1 - (((uhii ^ uloi) >> 126) & 2);
118+ uhii = uhii + 1 - (((uhii ^ uloi) >> 126) & 2);
119 sum.hi = @bitCast(uhii);
120 }
121 }
122diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig
123index dad60b725bc267a906e5c88abeb1ee342f818160..0f47a0b64781e423ec680cb162eb33e3ec51eddb 100644
124--- a/lib/std/Build/Step/Compile.zig
125+++ b/lib/std/Build/Step/Compile.zig
126@@ -192,6 +192,7 @@ want_lto: ?bool = null,
127
128 use_llvm: ?bool,
129 use_lld: ?bool,
130+use_new_linker: ?bool,
131
132 /// Corresponds to the `-fallow-so-scripts` / `-fno-allow-so-scripts` CLI
133 /// flags, overriding the global user setting provided to the `zig build`
134@@ -441,6 +442,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
135
136 .use_llvm = options.use_llvm,
137 .use_lld = options.use_lld,
138+ .use_new_linker = null,
139
140 .zig_process = null,
141 };
142@@ -1096,6 +1098,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
143
144 try addFlag(&zig_args, "llvm", compile.use_llvm);
145 try addFlag(&zig_args, "lld", compile.use_lld);
146+ try addFlag(&zig_args, "new-linker", compile.use_new_linker);
147
148 if (compile.root_module.resolved_target.?.query.ofmt) |ofmt| {
149 try zig_args.append(try std.fmt.allocPrint(arena, "-ofmt={s}", .{@tagName(ofmt)}));
150diff --git a/lib/std/elf.zig b/lib/std/elf.zig
151index bef80d782b20e759701c73a3fe85909985415f42..43b542c09e047ff0ae723f710750f396e18bbf08 100644
152--- a/lib/std/elf.zig
153+++ b/lib/std/elf.zig
154@@ -323,6 +323,8 @@ pub const PT_LOPROC = 0x70000000;
155 /// End of processor-specific
156 pub const PT_HIPROC = 0x7fffffff;
157
158+pub const PN_XNUM = 0xffff;
159+
160 /// Section header table entry unused
161 pub const SHT_NULL = 0;
162 /// Program data
163@@ -385,63 +387,149 @@ pub const SHT_HIUSER = 0xffffffff;
164 // Note type for .note.gnu.build_id
165 pub const NT_GNU_BUILD_ID = 3;
166
167-/// Local symbol
168-pub const STB_LOCAL = 0;
169-/// Global symbol
170-pub const STB_GLOBAL = 1;
171-/// Weak symbol
172-pub const STB_WEAK = 2;
173-/// Number of defined types
174-pub const STB_NUM = 3;
175-/// Start of OS-specific
176-pub const STB_LOOS = 10;
177-/// Unique symbol
178-pub const STB_GNU_UNIQUE = 10;
179-/// End of OS-specific
180-pub const STB_HIOS = 12;
181-/// Start of processor-specific
182-pub const STB_LOPROC = 13;
183-/// End of processor-specific
184-pub const STB_HIPROC = 15;
185-
186-pub const STB_MIPS_SPLIT_COMMON = 13;
187-
188-/// Symbol type is unspecified
189-pub const STT_NOTYPE = 0;
190-/// Symbol is a data object
191-pub const STT_OBJECT = 1;
192-/// Symbol is a code object
193-pub const STT_FUNC = 2;
194-/// Symbol associated with a section
195-pub const STT_SECTION = 3;
196-/// Symbol's name is file name
197-pub const STT_FILE = 4;
198-/// Symbol is a common data object
199-pub const STT_COMMON = 5;
200-/// Symbol is thread-local data object
201-pub const STT_TLS = 6;
202-/// Number of defined types
203-pub const STT_NUM = 7;
204-/// Start of OS-specific
205-pub const STT_LOOS = 10;
206-/// Symbol is indirect code object
207-pub const STT_GNU_IFUNC = 10;
208-/// End of OS-specific
209-pub const STT_HIOS = 12;
210-/// Start of processor-specific
211-pub const STT_LOPROC = 13;
212-/// End of processor-specific
213-pub const STT_HIPROC = 15;
214+/// Deprecated, use `@intFromEnum(std.elf.STB.LOCAL)`
215+pub const STB_LOCAL = @intFromEnum(STB.LOCAL);
216+/// Deprecated, use `@intFromEnum(std.elf.STB.GLOBAL)`
217+pub const STB_GLOBAL = @intFromEnum(STB.GLOBAL);
218+/// Deprecated, use `@intFromEnum(std.elf.STB.WEAK)`
219+pub const STB_WEAK = @intFromEnum(STB.WEAK);
220+/// Deprecated, use `std.elf.STB.NUM`
221+pub const STB_NUM = STB.NUM;
222+/// Deprecated, use `@intFromEnum(std.elf.STB.LOOS)`
223+pub const STB_LOOS = @intFromEnum(STB.LOOS);
224+/// Deprecated, use `@intFromEnum(std.elf.STB.GNU_UNIQUE)`
225+pub const STB_GNU_UNIQUE = @intFromEnum(STB.GNU_UNIQUE);
226+/// Deprecated, use `@intFromEnum(std.elf.STB.HIOS)`
227+pub const STB_HIOS = @intFromEnum(STB.HIOS);
228+/// Deprecated, use `@intFromEnum(std.elf.STB.LOPROC)`
229+pub const STB_LOPROC = @intFromEnum(STB.LOPROC);
230+/// Deprecated, use `@intFromEnum(std.elf.STB.HIPROC)`
231+pub const STB_HIPROC = @intFromEnum(STB.HIPROC);
232+
233+/// Deprecated, use `@intFromEnum(std.elf.STB.MIPS_SPLIT_COMMON)`
234+pub const STB_MIPS_SPLIT_COMMON = @intFromEnum(STB.MIBS_SPLIT_COMMON);
235+
236+/// Deprecated, use `@intFromEnum(std.elf.STT.NOTYPE)`
237+pub const STT_NOTYPE = @intFromEnum(STT.NOTYPE);
238+/// Deprecated, use `@intFromEnum(std.elf.STT.OBJECT)`
239+pub const STT_OBJECT = @intFromEnum(STT.OBJECT);
240+/// Deprecated, use `@intFromEnum(std.elf.STT.FUNC)`
241+pub const STT_FUNC = @intFromEnum(STT.FUNC);
242+/// Deprecated, use `@intFromEnum(std.elf.STT.SECTION)`
243+pub const STT_SECTION = @intFromEnum(STT.SECTION);
244+/// Deprecated, use `@intFromEnum(std.elf.STT.FILE)`
245+pub const STT_FILE = @intFromEnum(STT.FILE);
246+/// Deprecated, use `@intFromEnum(std.elf.STT.COMMON)`
247+pub const STT_COMMON = @intFromEnum(STT.COMMON);
248+/// Deprecated, use `@intFromEnum(std.elf.STT.TLS)`
249+pub const STT_TLS = @intFromEnum(STT.TLS);
250+/// Deprecated, use `std.elf.STT.NUM`
251+pub const STT_NUM = STT.NUM;
252+/// Deprecated, use `@intFromEnum(std.elf.STT.LOOS)`
253+pub const STT_LOOS = @intFromEnum(STT.LOOS);
254+/// Deprecated, use `@intFromEnum(std.elf.STT.GNU_IFUNC)`
255+pub const STT_GNU_IFUNC = @intFromEnum(STT.GNU_IFUNC);
256+/// Deprecated, use `@intFromEnum(std.elf.STT.HIOS)`
257+pub const STT_HIOS = @intFromEnum(STT.HIOS);
258+/// Deprecated, use `@intFromEnum(std.elf.STT.LOPROC)`
259+pub const STT_LOPROC = @intFromEnum(STT.LOPROC);
260+/// Deprecated, use `@intFromEnum(std.elf.STT.HIPROC)`
261+pub const STT_HIPROC = @intFromEnum(STT.HIPROC);
262+
263+/// Deprecated, use `@intFromEnum(std.elf.STT.SPARC_REGISTER)`
264+pub const STT_SPARC_REGISTER = @intFromEnum(STT.SPARC_REGISTER);
265+
266+/// Deprecated, use `@intFromEnum(std.elf.STT.PARISC_MILLICODE)`
267+pub const STT_PARISC_MILLICODE = @intFromEnum(STT.PARISC_MILLICODE);
268+
269+/// Deprecated, use `@intFromEnum(std.elf.STT.HP_OPAQUE)`
270+pub const STT_HP_OPAQUE = @intFromEnum(STT.HP_OPAQUE);
271+/// Deprecated, use `@intFromEnum(std.elf.STT.HP_STUB)`
272+pub const STT_HP_STUB = @intFromEnum(STT.HP_STUB);
273+
274+/// Deprecated, use `@intFromEnum(std.elf.STT.ARM_TFUNC)`
275+pub const STT_ARM_TFUNC = @intFromEnum(STT.ARM_TFUNC);
276+/// Deprecated, use `@intFromEnum(std.elf.STT.ARM_16BIT)`
277+pub const STT_ARM_16BIT = @intFromEnum(STT.ARM_16BIT);
278+
279+pub const STB = enum(u4) {
280+ /// Local symbol
281+ LOCAL = 0,
282+ /// Global symbol
283+ GLOBAL = 1,
284+ /// Weak symbol
285+ WEAK = 2,
286+ _,
287+
288+ /// Number of defined types
289+ pub const NUM = @typeInfo(STB).@"enum".fields.len;
290+
291+ /// Start of OS-specific
292+ pub const LOOS: STB = @enumFromInt(10);
293+ /// End of OS-specific
294+ pub const HIOS: STB = @enumFromInt(12);
295+
296+ /// Unique symbol
297+ pub const GNU_UNIQUE: STB = @enumFromInt(@intFromEnum(LOOS) + 0);
298+
299+ /// Start of processor-specific
300+ pub const LOPROC: STB = @enumFromInt(13);
301+ /// End of processor-specific
302+ pub const HIPROC: STB = @enumFromInt(15);
303+
304+ pub const MIPS_SPLIT_COMMON: STB = @enumFromInt(@intFromEnum(LOPROC) + 0);
305+};
306+
307+pub const STT = enum(u4) {
308+ /// Symbol type is unspecified
309+ NOTYPE = 0,
310+ /// Symbol is a data object
311+ OBJECT = 1,
312+ /// Symbol is a code object
313+ FUNC = 2,
314+ /// Symbol associated with a section
315+ SECTION = 3,
316+ /// Symbol's name is file name
317+ FILE = 4,
318+ /// Symbol is a common data object
319+ COMMON = 5,
320+ /// Symbol is thread-local data object
321+ TLS = 6,
322+ _,
323+
324+ /// Number of defined types
325+ pub const NUM = @typeInfo(STT).@"enum".fields.len;
326+
327+ /// Start of OS-specific
328+ pub const LOOS: STT = @enumFromInt(10);
329+ /// End of OS-specific
330+ pub const HIOS: STT = @enumFromInt(12);
331
332-pub const STT_SPARC_REGISTER = 13;
333+ /// Symbol is indirect code object
334+ pub const GNU_IFUNC: STT = @enumFromInt(@intFromEnum(LOOS) + 0);
335
336-pub const STT_PARISC_MILLICODE = 13;
337+ pub const HP_OPAQUE: STT = @enumFromInt(@intFromEnum(LOOS) + 1);
338+ pub const HP_STUB: STT = @enumFromInt(@intFromEnum(LOOS) + 2);
339
340-pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
341-pub const STT_HP_STUB = (STT_LOOS + 0x2);
342+ /// Start of processor-specific
343+ pub const LOPROC: STT = @enumFromInt(13);
344+ /// End of processor-specific
345+ pub const HIPROC: STT = @enumFromInt(15);
346
347-pub const STT_ARM_TFUNC = STT_LOPROC;
348-pub const STT_ARM_16BIT = STT_HIPROC;
349+ pub const SPARC_REGISTER: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
350+
351+ pub const PARISC_MILLICODE: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
352+
353+ pub const ARM_TFUNC: STT = @enumFromInt(@intFromEnum(LOPROC) + 0);
354+ pub const ARM_16BIT: STT = @enumFromInt(@intFromEnum(HIPROC) + 2);
355+};
356+
357+pub const STV = enum(u3) {
358+ DEFAULT = 0,
359+ INTERNAL = 1,
360+ HIDDEN = 2,
361+ PROTECTED = 3,
362+};
363
364 pub const MAGIC = "\x7fELF";
365
366@@ -534,15 +622,15 @@ pub const Header = struct {
367 const buf = try r.peek(@sizeOf(Elf64_Ehdr));
368
369 if (!mem.eql(u8, buf[0..4], MAGIC)) return error.InvalidElfMagic;
370- if (buf[EI_VERSION] != 1) return error.InvalidElfVersion;
371+ if (buf[EI.VERSION] != 1) return error.InvalidElfVersion;
372
373- const endian: std.builtin.Endian = switch (buf[EI_DATA]) {
374+ const endian: std.builtin.Endian = switch (buf[EI.DATA]) {
375 ELFDATA2LSB => .little,
376 ELFDATA2MSB => .big,
377 else => return error.InvalidElfEndian,
378 };
379
380- return switch (buf[EI_CLASS]) {
381+ return switch (buf[EI.CLASS]) {
382 ELFCLASS32 => .init(try r.takeStruct(Elf32_Ehdr, endian), endian),
383 ELFCLASS64 => .init(try r.takeStruct(Elf64_Ehdr, endian), endian),
384 else => return error.InvalidElfClass,
385@@ -559,8 +647,8 @@ pub const Header = struct {
386 else => @compileError("bad type"),
387 },
388 .endian = endian,
389- .os_abi = @enumFromInt(hdr.e_ident[EI_OSABI]),
390- .abi_version = hdr.e_ident[EI_ABIVERSION],
391+ .os_abi = @enumFromInt(hdr.e_ident[EI.OSABI]),
392+ .abi_version = hdr.e_ident[EI.ABIVERSION],
393 .type = hdr.e_type,
394 .machine = hdr.e_machine,
395 .entry = hdr.e_entry,
396@@ -683,38 +771,200 @@ fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr {
397 };
398 }
399
400-pub const ELFCLASSNONE = 0;
401-pub const ELFCLASS32 = 1;
402-pub const ELFCLASS64 = 2;
403-pub const ELFCLASSNUM = 3;
404-
405-pub const ELFDATANONE = 0;
406-pub const ELFDATA2LSB = 1;
407-pub const ELFDATA2MSB = 2;
408-pub const ELFDATANUM = 3;
409-
410-pub const EI_CLASS = 4;
411-pub const EI_DATA = 5;
412-pub const EI_VERSION = 6;
413-pub const EI_OSABI = 7;
414-pub const EI_ABIVERSION = 8;
415-pub const EI_PAD = 9;
416-
417-pub const EI_NIDENT = 16;
418+pub const EI = struct {
419+ pub const CLASS = 4;
420+ pub const DATA = 5;
421+ pub const VERSION = 6;
422+ pub const OSABI = 7;
423+ pub const ABIVERSION = 8;
424+ pub const PAD = 9;
425+ pub const NIDENT = 16;
426+};
427+
428+/// Deprecated, use `std.elf.EI.CLASS`
429+pub const EI_CLASS = EI.CLASS;
430+/// Deprecated, use `std.elf.EI.DATA`
431+pub const EI_DATA = EI.DATA;
432+/// Deprecated, use `std.elf.EI.VERSION`
433+pub const EI_VERSION = EI.VERSION;
434+/// Deprecated, use `std.elf.EI.OSABI`
435+pub const EI_OSABI = EI.OSABI;
436+/// Deprecated, use `std.elf.EI.ABIVERSION`
437+pub const EI_ABIVERSION = EI.ABIVERSION;
438+/// Deprecated, use `std.elf.EI.PAD`
439+pub const EI_PAD = EI.PAD;
440+/// Deprecated, use `std.elf.EI.NIDENT`
441+pub const EI_NIDENT = EI.NIDENT;
442
443 pub const Half = u16;
444 pub const Word = u32;
445 pub const Sword = i32;
446-pub const Elf32_Xword = u64;
447-pub const Elf32_Sxword = i64;
448-pub const Elf64_Xword = u64;
449+pub const Xword = u64;
450+pub const Sxword = i64;
451+pub const Section = u16;
452+pub const Elf32 = struct {
453+ pub const Addr = u32;
454+ pub const Off = u32;
455+ pub const Ehdr = extern struct {
456+ ident: [EI.NIDENT]u8,
457+ type: ET,
458+ machine: EM,
459+ version: Word,
460+ entry: Elf32.Addr,
461+ phoff: Elf32.Off,
462+ shoff: Elf32.Off,
463+ flags: Word,
464+ ehsize: Half,
465+ phentsize: Half,
466+ phnum: Half,
467+ shentsize: Half,
468+ shnum: Half,
469+ shstrndx: Half,
470+ };
471+ pub const Phdr = extern struct {
472+ type: Word,
473+ offset: Elf32.Off,
474+ vaddr: Elf32.Addr,
475+ paddr: Elf32.Addr,
476+ filesz: Word,
477+ memsz: Word,
478+ flags: PF,
479+ @"align": Word,
480+ };
481+ pub const Shdr = extern struct {
482+ name: Word,
483+ type: Word,
484+ flags: packed struct { shf: SHF },
485+ addr: Elf32.Addr,
486+ offset: Elf32.Off,
487+ size: Word,
488+ link: Word,
489+ info: Word,
490+ addralign: Word,
491+ entsize: Word,
492+ };
493+ pub const Chdr = extern struct {
494+ type: COMPRESS,
495+ size: Word,
496+ addralign: Word,
497+ };
498+ pub const Sym = extern struct {
499+ name: Word,
500+ value: Elf32.Addr,
501+ size: Word,
502+ info: Info,
503+ other: Other,
504+ shndx: Section,
505+
506+ pub const Info = packed struct(u8) {
507+ type: STT,
508+ bind: STB,
509+ };
510+
511+ pub const Other = packed struct(u8) {
512+ visibility: STV,
513+ unused: u5 = 0,
514+ };
515+ };
516+ comptime {
517+ assert(@sizeOf(Elf32.Ehdr) == 52);
518+ assert(@sizeOf(Elf32.Phdr) == 32);
519+ assert(@sizeOf(Elf32.Shdr) == 40);
520+ assert(@sizeOf(Elf32.Sym) == 16);
521+ }
522+};
523+pub const Elf64 = struct {
524+ pub const Addr = u64;
525+ pub const Off = u64;
526+ pub const Ehdr = extern struct {
527+ ident: [EI.NIDENT]u8,
528+ type: ET,
529+ machine: EM,
530+ version: Word,
531+ entry: Elf64.Addr,
532+ phoff: Elf64.Off,
533+ shoff: Elf64.Off,
534+ flags: Word,
535+ ehsize: Half,
536+ phentsize: Half,
537+ phnum: Half,
538+ shentsize: Half,
539+ shnum: Half,
540+ shstrndx: Half,
541+ };
542+ pub const Phdr = extern struct {
543+ type: Word,
544+ flags: PF,
545+ offset: Elf64.Off,
546+ vaddr: Elf64.Addr,
547+ paddr: Elf64.Addr,
548+ filesz: Xword,
549+ memsz: Xword,
550+ @"align": Xword,
551+ };
552+ pub const Shdr = extern struct {
553+ name: Word,
554+ type: Word,
555+ flags: packed struct { shf: SHF, unused: Word = 0 },
556+ addr: Elf64.Addr,
557+ offset: Elf64.Off,
558+ size: Xword,
559+ link: Word,
560+ info: Word,
561+ addralign: Xword,
562+ entsize: Xword,
563+ };
564+ pub const Chdr = extern struct {
565+ type: COMPRESS,
566+ reserved: Word = 0,
567+ size: Xword,
568+ addralign: Xword,
569+ };
570+ pub const Sym = extern struct {
571+ name: Word,
572+ info: Info,
573+ other: Other,
574+ shndx: Section,
575+ value: Elf64.Addr,
576+ size: Xword,
577+
578+ pub const Info = Elf32.Sym.Info;
579+ pub const Other = Elf32.Sym.Other;
580+ };
581+ comptime {
582+ assert(@sizeOf(Elf64.Ehdr) == 64);
583+ assert(@sizeOf(Elf64.Phdr) == 56);
584+ assert(@sizeOf(Elf64.Shdr) == 64);
585+ assert(@sizeOf(Elf64.Sym) == 24);
586+ }
587+};
588+pub const ElfN = switch (@sizeOf(usize)) {
589+ 4 => Elf32,
590+ 8 => Elf64,
591+ else => @compileError("expected pointer size of 32 or 64"),
592+};
593+
594+/// Deprecated, use `std.elf.Xword`
595+pub const Elf32_Xword = Xword;
596+/// Deprecated, use `std.elf.Sxword`
597+pub const Elf32_Sxword = Sxword;
598+/// Deprecated, use `std.elf.Xword`
599+pub const Elf64_Xword = Xword;
600+/// Deprecated, use `std.elf.Sxword`
601 pub const Elf64_Sxword = i64;
602+/// Deprecated, use `std.elf.Elf32.Addr`
603 pub const Elf32_Addr = u32;
604+/// Deprecated, use `std.elf.Elf64.Addr`
605 pub const Elf64_Addr = u64;
606+/// Deprecated, use `std.elf.Elf32.Off`
607 pub const Elf32_Off = u32;
608+/// Deprecated, use `std.elf.Elf64.Off`
609 pub const Elf64_Off = u64;
610+/// Deprecated, use `std.elf.Section`
611 pub const Elf32_Section = u16;
612+/// Deprecated, use `std.elf.Section`
613 pub const Elf64_Section = u16;
614+/// Deprecated, use `std.elf.Elf32.Ehdr`
615 pub const Elf32_Ehdr = extern struct {
616 e_ident: [EI_NIDENT]u8,
617 e_type: ET,
618@@ -731,8 +981,9 @@ pub const Elf32_Ehdr = extern struct {
619 e_shnum: Half,
620 e_shstrndx: Half,
621 };
622+/// Deprecated, use `std.elf.Elf64.Ehdr`
623 pub const Elf64_Ehdr = extern struct {
624- e_ident: [EI_NIDENT]u8,
625+ e_ident: [EI.NIDENT]u8,
626 e_type: ET,
627 e_machine: EM,
628 e_version: Word,
629@@ -747,6 +998,7 @@ pub const Elf64_Ehdr = extern struct {
630 e_shnum: Half,
631 e_shstrndx: Half,
632 };
633+/// Deprecated, use `std.elf.Elf32.Phdr`
634 pub const Elf32_Phdr = extern struct {
635 p_type: Word,
636 p_offset: Elf32_Off,
637@@ -757,6 +1009,7 @@ pub const Elf32_Phdr = extern struct {
638 p_flags: Word,
639 p_align: Word,
640 };
641+/// Deprecated, use `std.elf.Elf64.Phdr`
642 pub const Elf64_Phdr = extern struct {
643 p_type: Word,
644 p_flags: Word,
645@@ -767,6 +1020,7 @@ pub const Elf64_Phdr = extern struct {
646 p_memsz: Elf64_Xword,
647 p_align: Elf64_Xword,
648 };
649+/// Deprecated, use `std.elf.Elf32.Shdr`
650 pub const Elf32_Shdr = extern struct {
651 sh_name: Word,
652 sh_type: Word,
653@@ -779,6 +1033,7 @@ pub const Elf32_Shdr = extern struct {
654 sh_addralign: Word,
655 sh_entsize: Word,
656 };
657+/// Deprecated, use `std.elf.Elf64.Shdr`
658 pub const Elf64_Shdr = extern struct {
659 sh_name: Word,
660 sh_type: Word,
661@@ -791,17 +1046,20 @@ pub const Elf64_Shdr = extern struct {
662 sh_addralign: Elf64_Xword,
663 sh_entsize: Elf64_Xword,
664 };
665+/// Deprecated, use `std.elf.Elf32.Chdr`
666 pub const Elf32_Chdr = extern struct {
667 ch_type: COMPRESS,
668 ch_size: Word,
669 ch_addralign: Word,
670 };
671+/// Deprecated, use `std.elf.Elf64.Chdr`
672 pub const Elf64_Chdr = extern struct {
673 ch_type: COMPRESS,
674 ch_reserved: Word = 0,
675 ch_size: Elf64_Xword,
676 ch_addralign: Elf64_Xword,
677 };
678+/// Deprecated, use `std.elf.Elf32.Sym`
679 pub const Elf32_Sym = extern struct {
680 st_name: Word,
681 st_value: Elf32_Addr,
682@@ -817,6 +1075,7 @@ pub const Elf32_Sym = extern struct {
683 return @truncate(self.st_info >> 4);
684 }
685 };
686+/// Deprecated, use `std.elf.Elf64.Sym`
687 pub const Elf64_Sym = extern struct {
688 st_name: Word,
689 st_info: u8,
690@@ -1020,27 +1279,18 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct {
691 flags2: Word,
692 };
693
694-comptime {
695- assert(@sizeOf(Elf32_Ehdr) == 52);
696- assert(@sizeOf(Elf64_Ehdr) == 64);
697-
698- assert(@sizeOf(Elf32_Phdr) == 32);
699- assert(@sizeOf(Elf64_Phdr) == 56);
700-
701- assert(@sizeOf(Elf32_Shdr) == 40);
702- assert(@sizeOf(Elf64_Shdr) == 64);
703-}
704-
705 pub const Auxv = switch (@sizeOf(usize)) {
706 4 => Elf32_auxv_t,
707 8 => Elf64_auxv_t,
708 else => @compileError("expected pointer size of 32 or 64"),
709 };
710+/// Deprecated, use `std.elf.ElfN.Ehdr`
711 pub const Ehdr = switch (@sizeOf(usize)) {
712 4 => Elf32_Ehdr,
713 8 => Elf64_Ehdr,
714 else => @compileError("expected pointer size of 32 or 64"),
715 };
716+/// Deprecated, use `std.elf.ElfN.Phdr`
717 pub const Phdr = switch (@sizeOf(usize)) {
718 4 => Elf32_Phdr,
719 8 => Elf64_Phdr,
720@@ -1071,20 +1321,53 @@ pub const Shdr = switch (@sizeOf(usize)) {
721 8 => Elf64_Shdr,
722 else => @compileError("expected pointer size of 32 or 64"),
723 };
724+/// Deprecated, use `std.elf.ElfN.Chdr`
725 pub const Chdr = switch (@sizeOf(usize)) {
726 4 => Elf32_Chdr,
727 8 => Elf64_Chdr,
728 else => @compileError("expected pointer size of 32 or 64"),
729 };
730+/// Deprecated, use `std.elf.ElfN.Sym`
731 pub const Sym = switch (@sizeOf(usize)) {
732 4 => Elf32_Sym,
733 8 => Elf64_Sym,
734 else => @compileError("expected pointer size of 32 or 64"),
735 };
736-pub const Addr = switch (@sizeOf(usize)) {
737- 4 => Elf32_Addr,
738- 8 => Elf64_Addr,
739- else => @compileError("expected pointer size of 32 or 64"),
740+/// Deprecated, use `std.elf.ElfN.Addr`
741+pub const Addr = ElfN.Addr;
742+
743+/// Deprecated, use `@intFromEnum(std.elf.CLASS.NONE)`
744+pub const ELFCLASSNONE = @intFromEnum(CLASS.NONE);
745+/// Deprecated, use `@intFromEnum(std.elf.CLASS.@"32")`
746+pub const ELFCLASS32 = @intFromEnum(CLASS.@"32");
747+/// Deprecated, use `@intFromEnum(std.elf.CLASS.@"64")`
748+pub const ELFCLASS64 = @intFromEnum(CLASS.@"64");
749+/// Deprecated, use `@intFromEnum(std.elf.CLASS.NUM)`
750+pub const ELFCLASSNUM = CLASS.NUM;
751+pub const CLASS = enum(u8) {
752+ NONE = 0,
753+ @"32" = 1,
754+ @"64" = 2,
755+ _,
756+
757+ pub const NUM = @typeInfo(CLASS).@"enum".fields.len;
758+};
759+
760+/// Deprecated, use `@intFromEnum(std.elf.DATA.NONE)`
761+pub const ELFDATANONE = @intFromEnum(DATA.NONE);
762+/// Deprecated, use `@intFromEnum(std.elf.DATA.@"2LSB")`
763+pub const ELFDATA2LSB = @intFromEnum(DATA.@"2LSB");
764+/// Deprecated, use `@intFromEnum(std.elf.DATA.@"2MSB")`
765+pub const ELFDATA2MSB = @intFromEnum(DATA.@"2MSB");
766+/// Deprecated, use `@intFromEnum(std.elf.DATA.NUM)`
767+pub const ELFDATANUM = DATA.NUM;
768+pub const DATA = enum(u8) {
769+ NONE = 0,
770+ @"2LSB" = 1,
771+ @"2MSB" = 2,
772+ _,
773+
774+ pub const NUM = @typeInfo(DATA).@"enum".fields.len;
775 };
776
777 pub const OSABI = enum(u8) {
778@@ -1718,6 +2001,108 @@ pub const SHF_MIPS_STRING = 0x80000000;
779 /// Make code section unreadable when in execute-only mode
780 pub const SHF_ARM_PURECODE = 0x2000000;
781
782+pub const SHF = packed struct(Word) {
783+ /// Section data should be writable during execution.
784+ WRITE: bool = false,
785+ /// Section occupies memory during program execution.
786+ ALLOC: bool = false,
787+ /// Section contains executable machine instructions.
788+ EXECINSTR: bool = false,
789+ unused3: u1 = 0,
790+ /// The data in this section may be merged.
791+ MERGE: bool = false,
792+ /// The data in this section is null-terminated strings.
793+ STRINGS: bool = false,
794+ /// A field in this section holds a section header table index.
795+ INFO_LINK: bool = false,
796+ /// Adds special ordering requirements for link editors.
797+ LINK_ORDER: bool = false,
798+ /// This section requires special OS-specific processing to avoid incorrect behavior.
799+ OS_NONCONFORMING: bool = false,
800+ /// This section is a member of a section group.
801+ GROUP: bool = false,
802+ /// This section holds Thread-Local Storage.
803+ TLS: bool = false,
804+ /// Identifies a section containing compressed data.
805+ COMPRESSED: bool = false,
806+ unused12: u8 = 0,
807+ OS: packed union {
808+ MASK: u8,
809+ GNU: packed struct(u8) {
810+ unused0: u1 = 0,
811+ /// Not to be GCed by the linker
812+ RETAIN: bool = false,
813+ unused2: u6 = 0,
814+ },
815+ MIPS: packed struct(u8) {
816+ unused0: u4 = 0,
817+ /// Section contains text/data which may be replicated in other sections.
818+ /// Linker must retain only one copy.
819+ NODUPES: bool = false,
820+ /// Linker must generate implicit hidden weak names.
821+ NAMES: bool = false,
822+ /// Section data local to process.
823+ LOCAL: bool = false,
824+ /// Do not strip this section.
825+ NOSTRIP: bool = false,
826+ },
827+ ARM: packed struct(u8) {
828+ unused0: u5 = 0,
829+ /// Make code section unreadable when in execute-only mode
830+ PURECODE: bool = false,
831+ unused6: u2 = 0,
832+ },
833+ } = .{ .MASK = 0 },
834+ PROC: packed union {
835+ MASK: u4,
836+ XCORE: packed struct(u4) {
837+ /// All sections with the "d" flag are grouped together by the linker to form
838+ /// the data section and the dp register is set to the start of the section by
839+ /// the boot code.
840+ DP_SECTION: bool = false,
841+ /// All sections with the "c" flag are grouped together by the linker to form
842+ /// the constant pool and the cp register is set to the start of the constant
843+ /// pool by the boot code.
844+ CP_SECTION: bool = false,
845+ unused2: u1 = 0,
846+ /// This section is excluded from the final executable or shared library.
847+ EXCLUDE: bool = false,
848+ },
849+ X86_64: packed struct(u4) {
850+ /// If an object file section does not have this flag set, then it may not hold
851+ /// more than 2GB and can be freely referred to in objects using smaller code
852+ /// models. Otherwise, only objects using larger code models can refer to them.
853+ /// For example, a medium code model object can refer to data in a section that
854+ /// sets this flag besides being able to refer to data in a section that does
855+ /// not set it; likewise, a small code model object can refer only to code in a
856+ /// section that does not set this flag.
857+ LARGE: bool = false,
858+ unused1: u2 = 0,
859+ /// This section is excluded from the final executable or shared library.
860+ EXCLUDE: bool = false,
861+ },
862+ HEX: packed struct(u4) {
863+ /// All sections with the GPREL flag are grouped into a global data area
864+ /// for faster accesses
865+ GPREL: bool = false,
866+ unused1: u2 = 0,
867+ /// This section is excluded from the final executable or shared library.
868+ EXCLUDE: bool = false,
869+ },
870+ MIPS: packed struct(u4) {
871+ /// All sections with the GPREL flag are grouped into a global data area
872+ /// for faster accesses
873+ GPREL: bool = false,
874+ /// This section should be merged.
875+ MERGE: bool = false,
876+ /// Address size to be inferred from section entry size.
877+ ADDR: bool = false,
878+ /// Section data is string data by default.
879+ STRING: bool = false,
880+ },
881+ } = .{ .MASK = 0 },
882+};
883+
884 /// Execute
885 pub const PF_X = 1;
886
887@@ -1733,6 +2118,19 @@ pub const PF_MASKOS = 0x0ff00000;
888 /// Bits for processor-specific semantics.
889 pub const PF_MASKPROC = 0xf0000000;
890
891+pub const PF = packed struct(Word) {
892+ X: bool = false,
893+ W: bool = false,
894+ R: bool = false,
895+ unused3: u17 = 0,
896+ OS: packed union {
897+ MASK: u8,
898+ } = .{ .MASK = 0 },
899+ PROC: packed union {
900+ MASK: u4,
901+ } = .{ .MASK = 0 },
902+};
903+
904 /// Undefined section
905 pub const SHN_UNDEF = 0;
906 /// Start of reserved indices
907@@ -2303,13 +2701,6 @@ pub const R_PPC64 = enum(u32) {
908 _,
909 };
910
911-pub const STV = enum(u3) {
912- DEFAULT = 0,
913- INTERNAL = 1,
914- HIDDEN = 2,
915- PROTECTED = 3,
916-};
917-
918 pub const ar_hdr = extern struct {
919 /// Member file name, sometimes / terminated.
920 ar_name: [16]u8,
921diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig
922index b2116c1a7429b5791d8e39d66dd7e66ad64337e0..55f1fd14cb6cfa8421a142a142802710c9944bd5 100644
923--- a/lib/std/zig/system.zig
924+++ b/lib/std/zig/system.zig
925@@ -516,15 +516,15 @@ pub fn abiAndDynamicLinkerFromFile(
926 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
927 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
928 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
929- const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
930+ const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
931 elf.ELFDATA2LSB => .little,
932 elf.ELFDATA2MSB => .big,
933 else => return error.InvalidElfEndian,
934 };
935 const need_bswap = elf_endian != native_endian;
936- if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
937+ if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
938
939- const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
940+ const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
941 elf.ELFCLASS32 => false,
942 elf.ELFCLASS64 => true,
943 else => return error.InvalidElfClass,
944@@ -920,15 +920,15 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
945 const hdr32: *elf.Elf32_Ehdr = @ptrCast(&hdr_buf);
946 const hdr64: *elf.Elf64_Ehdr = @ptrCast(&hdr_buf);
947 if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
948- const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) {
949+ const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI.DATA]) {
950 elf.ELFDATA2LSB => .little,
951 elf.ELFDATA2MSB => .big,
952 else => return error.InvalidElfEndian,
953 };
954 const need_bswap = elf_endian != native_endian;
955- if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
956+ if (hdr32.e_ident[elf.EI.VERSION] != 1) return error.InvalidElfVersion;
957
958- const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) {
959+ const is_64 = switch (hdr32.e_ident[elf.EI.CLASS]) {
960 elf.ELFCLASS32 => false,
961 elf.ELFCLASS64 => true,
962 else => return error.InvalidElfClass,
963diff --git a/src/Compilation.zig b/src/Compilation.zig
964index 34953ed11a12e334a1ecd995de399421a896a5bb..bf7246b8d8b8fe3df2c4ac1c4fb6124fe55e134b 100644
965--- a/src/Compilation.zig
966+++ b/src/Compilation.zig
967@@ -177,7 +177,6 @@ debug_compiler_runtime_libs: bool,
968 debug_compile_errors: bool,
969 /// Do not check this field directly. Instead, use the `debugIncremental` wrapper function.
970 debug_incremental: bool,
971-incremental: bool,
972 alloc_failure_occurred: bool = false,
973 last_update_was_cache_hit: bool = false,
974
975@@ -256,7 +255,9 @@ mutex: if (builtin.single_threaded) struct {
976 test_filters: []const []const u8,
977
978 link_task_wait_group: WaitGroup = .{},
979-link_prog_node: std.Progress.Node = std.Progress.Node.none,
980+link_prog_node: std.Progress.Node = .none,
981+link_uav_prog_node: std.Progress.Node = .none,
982+link_lazy_prog_node: std.Progress.Node = .none,
983
984 llvm_opt_bisect_limit: c_int,
985
986@@ -1746,7 +1747,6 @@ pub const CreateOptions = struct {
987 debug_compiler_runtime_libs: bool = false,
988 debug_compile_errors: bool = false,
989 debug_incremental: bool = false,
990- incremental: bool = false,
991 /// Normally when you create a `Compilation`, Zig will automatically build
992 /// and link in required dependencies, such as compiler-rt and libc. When
993 /// building such dependencies themselves, this flag must be set to avoid
994@@ -1982,6 +1982,7 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
995 };
996 if (have_zcu and (!need_llvm or use_llvm)) {
997 if (output_mode == .Obj) break :s .zcu;
998+ if (options.config.use_new_linker) break :s .zcu;
999 switch (target_util.zigBackend(target, use_llvm)) {
1000 else => {},
1001 .stage2_aarch64, .stage2_x86_64 => if (target.ofmt == .coff) {
1002@@ -2188,8 +2189,8 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
1003 .inherited = .{},
1004 .global = options.config,
1005 .parent = options.root_mod,
1006- }) catch |err| return switch (err) {
1007- error.OutOfMemory => |e| return e,
1008+ }) catch |err| switch (err) {
1009+ error.OutOfMemory => return error.OutOfMemory,
1010 // None of these are possible because the configuration matches the root module
1011 // which already passed these checks.
1012 error.ValgrindUnsupportedOnTarget => unreachable,
1013@@ -2266,7 +2267,6 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
1014 .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
1015 .debug_compile_errors = options.debug_compile_errors,
1016 .debug_incremental = options.debug_incremental,
1017- .incremental = options.incremental,
1018 .root_name = root_name,
1019 .sysroot = sysroot,
1020 .windows_libs = .empty,
1021@@ -2409,6 +2409,8 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options
1022 // Synchronize with other matching comments: ZigOnlyHashStuff
1023 hash.add(use_llvm);
1024 hash.add(options.config.use_lib_llvm);
1025+ hash.add(options.config.use_lld);
1026+ hash.add(options.config.use_new_linker);
1027 hash.add(options.config.dll_export_fns);
1028 hash.add(options.config.is_test);
1029 hash.addListOfBytes(options.test_filters);
1030@@ -3075,14 +3077,29 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
1031
1032 // The linker progress node is set up here instead of in `performAllTheWork`, because
1033 // we also want it around during `flush`.
1034- const have_link_node = comp.bin_file != null;
1035- if (have_link_node) {
1036+ if (comp.bin_file) |lf| {
1037 comp.link_prog_node = main_progress_node.start("Linking", 0);
1038+ if (lf.cast(.elf2)) |elf| {
1039+ comp.link_prog_node.increaseEstimatedTotalItems(3);
1040+ comp.link_uav_prog_node = comp.link_prog_node.start("Constants", 0);
1041+ comp.link_lazy_prog_node = comp.link_prog_node.start("Synthetics", 0);
1042+ elf.mf.update_prog_node = comp.link_prog_node.start("Relocations", elf.mf.updates.items.len);
1043+ }
1044 }
1045- defer if (have_link_node) {
1046+ defer {
1047 comp.link_prog_node.end();
1048 comp.link_prog_node = .none;
1049- };
1050+ comp.link_uav_prog_node.end();
1051+ comp.link_uav_prog_node = .none;
1052+ comp.link_lazy_prog_node.end();
1053+ comp.link_lazy_prog_node = .none;
1054+ if (comp.bin_file) |lf| {
1055+ if (lf.cast(.elf2)) |elf| {
1056+ elf.mf.update_prog_node.end();
1057+ elf.mf.update_prog_node = .none;
1058+ }
1059+ }
1060+ }
1061
1062 try comp.performAllTheWork(main_progress_node);
1063
1064@@ -3100,6 +3117,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
1065 try pt.populateTestFunctions();
1066 }
1067
1068+ link.updateErrorData(pt);
1069+
1070 try pt.processExports();
1071 }
1072
1073@@ -3474,6 +3493,8 @@ fn addNonIncrementalStuffToCacheManifest(
1074
1075 man.hash.add(comp.config.use_llvm);
1076 man.hash.add(comp.config.use_lib_llvm);
1077+ man.hash.add(comp.config.use_lld);
1078+ man.hash.add(comp.config.use_new_linker);
1079 man.hash.add(comp.config.is_test);
1080 man.hash.add(comp.config.import_memory);
1081 man.hash.add(comp.config.export_memory);
1082@@ -4073,7 +4094,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
1083 defer sorted_failed_analysis.deinit(gpa);
1084 var added_any_analysis_error = false;
1085 for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| {
1086- if (comp.incremental) {
1087+ if (comp.config.incremental) {
1088 const refs = try zcu.resolveReferences();
1089 if (!refs.contains(anal_unit)) continue;
1090 }
1091@@ -4240,7 +4261,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
1092
1093 // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
1094 // very common way for incremental compilation bugs to manifest, so let's always check it.
1095- if (comp.zcu) |zcu| if (comp.incremental and bundle.root_list.items.len == 0) {
1096+ if (comp.zcu) |zcu| if (comp.config.incremental and bundle.root_list.items.len == 0) {
1097 for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
1098 const refs = try zcu.resolveReferences();
1099 var ref = refs.get(failed_unit) orelse continue;
1100@@ -4949,7 +4970,7 @@ fn performAllTheWork(
1101 tr.stats.n_reachable_files = @intCast(zcu.alive_files.count());
1102 }
1103
1104- if (comp.incremental) {
1105+ if (comp.config.incremental) {
1106 const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
1107 defer update_zir_refs_node.end();
1108 try pt.updateZirRefs();
1109diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig
1110index d8751251da9e24db98c79f7964e0ea1a9bde78c8..45f7e509de609a4ab10651d27e49a1cb645eb2b6 100644
1111--- a/src/Compilation/Config.zig
1112+++ b/src/Compilation/Config.zig
1113@@ -49,6 +49,8 @@ use_lib_llvm: bool,
1114 use_lld: bool,
1115 c_frontend: CFrontend,
1116 lto: std.zig.LtoMode,
1117+use_new_linker: bool,
1118+incremental: bool,
1119 /// WASI-only. Type of WASI execution model ("command" or "reactor").
1120 /// Always set to `command` for non-WASI targets.
1121 wasi_exec_model: std.builtin.WasiExecModel,
1122@@ -104,6 +106,8 @@ pub const Options = struct {
1123 use_lld: ?bool = null,
1124 use_clang: ?bool = null,
1125 lto: ?std.zig.LtoMode = null,
1126+ use_new_linker: ?bool = null,
1127+ incremental: bool = false,
1128 /// WASI-only. Type of WASI execution model ("command" or "reactor").
1129 wasi_exec_model: ?std.builtin.WasiExecModel = null,
1130 import_memory: ?bool = null,
1131@@ -147,6 +151,8 @@ pub const ResolveError = error{
1132 LldUnavailable,
1133 ClangUnavailable,
1134 DllExportFnsRequiresWindows,
1135+ NewLinkerIncompatibleWithLld,
1136+ NewLinkerIncompatibleObjectFormat,
1137 };
1138
1139 pub fn resolve(options: Options) ResolveError!Config {
1140@@ -458,6 +464,22 @@ pub fn resolve(options: Options) ResolveError!Config {
1141 break :b .none;
1142 };
1143
1144+ const use_new_linker = b: {
1145+ if (use_lld) {
1146+ if (options.use_new_linker == true) return error.NewLinkerIncompatibleWithLld;
1147+ break :b false;
1148+ }
1149+
1150+ if (!target_util.hasNewLinkerSupport(target.ofmt)) {
1151+ if (options.use_new_linker == true) return error.NewLinkerIncompatibleObjectFormat;
1152+ break :b false;
1153+ }
1154+
1155+ if (options.use_new_linker) |x| break :b x;
1156+
1157+ break :b options.incremental;
1158+ };
1159+
1160 const root_strip = b: {
1161 if (options.root_strip) |x| break :b x;
1162 if (root_optimize_mode == .ReleaseSmall) break :b true;
1163@@ -531,6 +553,8 @@ pub fn resolve(options: Options) ResolveError!Config {
1164 .root_error_tracing = root_error_tracing,
1165 .pie = pie,
1166 .lto = lto,
1167+ .use_new_linker = use_new_linker,
1168+ .incremental = options.incremental,
1169 .import_memory = import_memory,
1170 .export_memory = export_memory,
1171 .shared_memory = shared_memory,
1172diff --git a/src/InternPool.zig b/src/InternPool.zig
1173index 996549774294455b642ef3286d3d20be0bd404f5..f4344af86d46be017beacf31e90c581493517b0f 100644
1174--- a/src/InternPool.zig
1175+++ b/src/InternPool.zig
1176@@ -6424,14 +6424,25 @@ pub const Alignment = enum(u6) {
1177 return n + 1;
1178 }
1179
1180+ pub fn toStdMem(a: Alignment) std.mem.Alignment {
1181+ assert(a != .none);
1182+ return @enumFromInt(@intFromEnum(a));
1183+ }
1184+
1185+ pub fn fromStdMem(a: std.mem.Alignment) Alignment {
1186+ const r: Alignment = @enumFromInt(@intFromEnum(a));
1187+ assert(r != .none);
1188+ return r;
1189+ }
1190+
1191 const LlvmBuilderAlignment = std.zig.llvm.Builder.Alignment;
1192
1193- pub fn toLlvm(this: @This()) LlvmBuilderAlignment {
1194- return @enumFromInt(@intFromEnum(this));
1195+ pub fn toLlvm(a: Alignment) LlvmBuilderAlignment {
1196+ return @enumFromInt(@intFromEnum(a));
1197 }
1198
1199- pub fn fromLlvm(other: LlvmBuilderAlignment) @This() {
1200- return @enumFromInt(@intFromEnum(other));
1201+ pub fn fromLlvm(a: LlvmBuilderAlignment) Alignment {
1202+ return @enumFromInt(@intFromEnum(a));
1203 }
1204 };
1205
1206diff --git a/src/Sema.zig b/src/Sema.zig
1207index 946e890ea3566e0cc1ab2aae453d389ecfab702b..d0576de33d4d3077f7fa177ba4e0b8e9b438ee69 100644
1208--- a/src/Sema.zig
1209+++ b/src/Sema.zig
1210@@ -3032,7 +3032,7 @@ fn zirStructDecl(
1211 });
1212 errdefer pt.destroyNamespace(new_namespace_index);
1213
1214- if (pt.zcu.comp.incremental) {
1215+ if (pt.zcu.comp.config.incremental) {
1216 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
1217 }
1218
1219@@ -3430,7 +3430,7 @@ fn zirUnionDecl(
1220 });
1221 errdefer pt.destroyNamespace(new_namespace_index);
1222
1223- if (pt.zcu.comp.incremental) {
1224+ if (pt.zcu.comp.config.incremental) {
1225 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
1226 }
1227
1228@@ -6217,7 +6217,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
1229 if (ptr_info.byte_offset != 0) {
1230 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
1231 }
1232- if (options.linkage == .internal) return;
1233+ if (zcu.llvm_object != null and options.linkage == .internal) return;
1234 const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
1235 if (!try sema.validateExternType(export_ty, .other)) {
1236 return sema.failWithOwnedErrorMsg(block, msg: {
1237@@ -6256,7 +6256,7 @@ pub fn analyzeExport(
1238 const zcu = pt.zcu;
1239 const ip = &zcu.intern_pool;
1240
1241- if (options.linkage == .internal)
1242+ if (zcu.llvm_object != null and options.linkage == .internal)
1243 return;
1244
1245 try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
1246@@ -7709,7 +7709,7 @@ fn analyzeCall(
1247 // TODO: comptime call memoization is currently not supported under incremental compilation
1248 // since dependencies are not marked on callers. If we want to keep this around (we should
1249 // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
1250- if (zcu.comp.incremental) break :m false;
1251+ if (zcu.comp.config.incremental) break :m false;
1252 if (!block.isComptime()) break :m false;
1253 for (args) |a| {
1254 const val = (try sema.resolveValue(a)).?;
1255@@ -31208,7 +31208,7 @@ fn addReferenceEntry(
1256 .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced
1257 else => {},
1258 }
1259- if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
1260+ if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return;
1261 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
1262 if (gop.found_existing) return;
1263 try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: {
1264@@ -31225,7 +31225,7 @@ pub fn addTypeReferenceEntry(
1265 referenced_type: InternPool.Index,
1266 ) !void {
1267 const zcu = sema.pt.zcu;
1268- if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
1269+ if (!zcu.comp.config.incremental and zcu.comp.reference_trace == 0) return;
1270 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
1271 if (gop.found_existing) return;
1272 try zcu.addTypeReference(sema.owner, referenced_type, src);
1273@@ -36875,7 +36875,7 @@ fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool
1274
1275 pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
1276 const pt = sema.pt;
1277- if (!pt.zcu.comp.incremental) return;
1278+ if (!pt.zcu.comp.config.incremental) return;
1279
1280 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
1281 if (gop.found_existing) return;
1282diff --git a/src/Value.zig b/src/Value.zig
1283index 8de562176107ae28fb15ce2e6ed241aa7f176e16..381eacf45ab2389c950aad0dd9f0ed886e9a8bcb 100644
1284--- a/src/Value.zig
1285+++ b/src/Value.zig
1286@@ -23,7 +23,7 @@ pub fn format(val: Value, writer: *std.Io.Writer) !void {
1287
1288 /// This is a debug function. In order to print values in a meaningful way
1289 /// we also need access to the type.
1290-pub fn dump(start_val: Value, w: std.Io.Writer) std.Io.Writer.Error!void {
1291+pub fn dump(start_val: Value, w: *std.Io.Writer) std.Io.Writer.Error!void {
1292 try w.print("(interned: {})", .{start_val.toIntern()});
1293 }
1294
1295diff --git a/src/Zcu.zig b/src/Zcu.zig
1296index 706bdb51655409a6e2c23e36d580d0c81c01f350..8e9e9c29025c4e82af1d14ece0eca9b7d9bd06e4 100644
1297--- a/src/Zcu.zig
1298+++ b/src/Zcu.zig
1299@@ -3166,7 +3166,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
1300 }
1301
1302 pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
1303- if (!zcu.comp.incremental) return null;
1304+ if (!zcu.comp.config.incremental) return null;
1305
1306 if (zcu.outdated.count() == 0) {
1307 // Any units in `potentially_outdated` must just be stuck in loops with one another: none of those
1308diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
1309index 77cddb204e82eeeba3b349b0ed2216974d06f14c..32782e7e8952462d2991ff0b3bf3bbe8e0b72c64 100644
1310--- a/src/Zcu/PerThread.zig
1311+++ b/src/Zcu/PerThread.zig
1312@@ -1815,7 +1815,7 @@ fn createFileRootStruct(
1313 wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none);
1314 ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
1315
1316- if (zcu.comp.incremental) {
1317+ if (zcu.comp.config.incremental) {
1318 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
1319 }
1320
1321diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
1322index 7b13cfc90d68b760251c236bd43890858a088dd9..fe40ba4bbb16ffcbcc2768af9a6f8a57600c1e4f 100644
1323--- a/src/arch/riscv64/CodeGen.zig
1324+++ b/src/arch/riscv64/CodeGen.zig
1325@@ -858,9 +858,11 @@ pub fn generateLazy(
1326 pt: Zcu.PerThread,
1327 src_loc: Zcu.LazySrcLoc,
1328 lazy_sym: link.File.LazySymbol,
1329- code: *std.ArrayListUnmanaged(u8),
1330+ atom_index: u32,
1331+ w: *std.Io.Writer,
1332 debug_output: link.File.DebugInfoOutput,
1333-) CodeGenError!void {
1334+) (CodeGenError || std.Io.Writer.Error)!void {
1335+ _ = atom_index;
1336 const comp = bin_file.comp;
1337 const gpa = comp.gpa;
1338 const mod = comp.root_mod;
1339@@ -914,7 +916,7 @@ pub fn generateLazy(
1340 },
1341 .bin_file = bin_file,
1342 .debug_output = debug_output,
1343- .code = code,
1344+ .w = w,
1345 .prev_di_pc = undefined, // no debug info yet
1346 .prev_di_line = undefined, // no debug info yet
1347 .prev_di_column = undefined, // no debug info yet
1348diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig
1349index 2e72aff941072c7e5d7164eace99b5d919dc4ece..64a476007cf9881e5101c5c945158252b1ba5b86 100644
1350--- a/src/arch/riscv64/Emit.zig
1351+++ b/src/arch/riscv64/Emit.zig
1352@@ -3,7 +3,7 @@
1353 bin_file: *link.File,
1354 lower: Lower,
1355 debug_output: link.File.DebugInfoOutput,
1356-code: *std.ArrayListUnmanaged(u8),
1357+w: *std.Io.Writer,
1358
1359 prev_di_line: u32,
1360 prev_di_column: u32,
1361@@ -13,7 +13,7 @@ prev_di_pc: usize,
1362 code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
1363 relocs: std.ArrayListUnmanaged(Reloc) = .empty,
1364
1365-pub const Error = Lower.Error || error{
1366+pub const Error = Lower.Error || std.Io.Writer.Error || error{
1367 EmitFail,
1368 };
1369
1370@@ -25,13 +25,13 @@ pub fn emitMir(emit: *Emit) Error!void {
1371 try emit.code_offset_mapping.putNoClobber(
1372 emit.lower.allocator,
1373 mir_index,
1374- @intCast(emit.code.items.len),
1375+ @intCast(emit.w.end),
1376 );
1377 const lowered = try emit.lower.lowerMir(mir_index, .{ .allow_frame_locs = true });
1378 var lowered_relocs = lowered.relocs;
1379 for (lowered.insts, 0..) |lowered_inst, lowered_index| {
1380- const start_offset: u32 = @intCast(emit.code.items.len);
1381- std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), lowered_inst.toU32(), .little);
1382+ const start_offset: u32 = @intCast(emit.w.end);
1383+ try emit.w.writeInt(u32, lowered_inst.toU32(), .little);
1384
1385 while (lowered_relocs.len > 0 and
1386 lowered_relocs[0].lowered_inst_index == lowered_index) : ({
1387@@ -175,7 +175,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
1388 return emit.fail("relocation target not found!", .{});
1389
1390 const disp = @as(i32, @intCast(target)) - @as(i32, @intCast(reloc.source));
1391- const code: *[4]u8 = emit.code.items[reloc.source + reloc.offset ..][0..4];
1392+ const code = emit.w.buffered()[reloc.source + reloc.offset ..][0..4];
1393
1394 switch (reloc.fmt) {
1395 .J => riscv_util.writeInstJ(code, @bitCast(disp)),
1396@@ -187,7 +187,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
1397
1398 fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
1399 const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
1400- const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
1401+ const delta_pc: usize = emit.w.end - emit.prev_di_pc;
1402 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
1403 switch (emit.debug_output) {
1404 .dwarf => |dw| {
1405@@ -196,7 +196,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
1406 try dw.advancePCAndLine(delta_line, delta_pc);
1407 emit.prev_di_line = line;
1408 emit.prev_di_column = column;
1409- emit.prev_di_pc = emit.code.items.len;
1410+ emit.prev_di_pc = emit.w.end;
1411 },
1412 .none => {},
1413 }
1414diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig
1415index 6fb8e23a98c3b0e89ee5139429fc11626fb2ecc7..204ab6a3c06226635b1619b60c205f02f8447884 100644
1416--- a/src/arch/riscv64/Mir.zig
1417+++ b/src/arch/riscv64/Mir.zig
1418@@ -109,9 +109,11 @@ pub fn emit(
1419 pt: Zcu.PerThread,
1420 src_loc: Zcu.LazySrcLoc,
1421 func_index: InternPool.Index,
1422- code: *std.ArrayListUnmanaged(u8),
1423+ atom_index: u32,
1424+ w: *std.Io.Writer,
1425 debug_output: link.File.DebugInfoOutput,
1426-) codegen.CodeGenError!void {
1427+) (codegen.CodeGenError || std.Io.Writer.Error)!void {
1428+ _ = atom_index;
1429 const zcu = pt.zcu;
1430 const comp = zcu.comp;
1431 const gpa = comp.gpa;
1432@@ -132,7 +134,7 @@ pub fn emit(
1433 },
1434 .bin_file = lf,
1435 .debug_output = debug_output,
1436- .code = code,
1437+ .w = w,
1438 .prev_di_pc = 0,
1439 .prev_di_line = func.lbrace_line,
1440 .prev_di_column = func.lbrace_column,
1441diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig
1442index d41f1cdf07aa54b3c55a9917826a7b55a7da23b0..26282b09ab4f5c831cea32e9c9dbf8d6e0b2256a 100644
1443--- a/src/arch/sparc64/Emit.zig
1444+++ b/src/arch/sparc64/Emit.zig
1445@@ -21,7 +21,7 @@ debug_output: link.File.DebugInfoOutput,
1446 target: *const std.Target,
1447 err_msg: ?*ErrorMsg = null,
1448 src_loc: Zcu.LazySrcLoc,
1449-code: *std.ArrayListUnmanaged(u8),
1450+w: *std.Io.Writer,
1451
1452 prev_di_line: u32,
1453 prev_di_column: u32,
1454@@ -40,7 +40,7 @@ branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUn
1455 /// instruction
1456 code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .empty,
1457
1458-const InnerError = error{
1459+const InnerError = std.Io.Writer.Error || error{
1460 OutOfMemory,
1461 EmitFail,
1462 };
1463@@ -292,7 +292,7 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
1464 .bpcc => switch (tag) {
1465 .bpcc => {
1466 const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int;
1467- const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_int.inst).?)) - @as(i64, @intCast(emit.code.items.len));
1468+ const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_int.inst).?)) - @as(i64, @intCast(emit.w.end));
1469 log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset });
1470
1471 try emit.writeInstruction(
1472@@ -310,7 +310,7 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
1473 .bpr => switch (tag) {
1474 .bpr => {
1475 const branch_predict_reg = emit.mir.instructions.items(.data)[inst].branch_predict_reg;
1476- const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_reg.inst).?)) - @as(i64, @intCast(emit.code.items.len));
1477+ const offset = @as(i64, @intCast(emit.code_offset_mapping.get(branch_predict_reg.inst).?)) - @as(i64, @intCast(emit.w.end));
1478 log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset });
1479
1480 try emit.writeInstruction(
1481@@ -494,13 +494,13 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
1482
1483 fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
1484 const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
1485- const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
1486+ const delta_pc: usize = emit.w.end - emit.prev_di_pc;
1487 switch (emit.debug_output) {
1488 .dwarf => |dbg_out| {
1489 try dbg_out.advancePCAndLine(delta_line, delta_pc);
1490 emit.prev_di_line = line;
1491 emit.prev_di_column = column;
1492- emit.prev_di_pc = emit.code.items.len;
1493+ emit.prev_di_pc = emit.w.end;
1494 },
1495 else => {},
1496 }
1497@@ -675,13 +675,8 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
1498 }
1499
1500 fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
1501- const comp = emit.bin_file.comp;
1502- const gpa = comp.gpa;
1503-
1504 // SPARCv9 instructions are always arranged in BE regardless of the
1505 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
1506 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
1507- const endian: Endian = .big;
1508-
1509- std.mem.writeInt(u32, try emit.code.addManyAsArray(gpa, 4), instruction.toU32(), endian);
1510+ try emit.w.writeInt(u32, instruction.toU32(), .big);
1511 }
1512diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig
1513index 842ac10fed1af8fbaeadb7b2c0404a20f806abc0..41d135eb831fefe51afd3fde7925596fce9d441b 100644
1514--- a/src/arch/sparc64/Mir.zig
1515+++ b/src/arch/sparc64/Mir.zig
1516@@ -380,9 +380,11 @@ pub fn emit(
1517 pt: Zcu.PerThread,
1518 src_loc: Zcu.LazySrcLoc,
1519 func_index: InternPool.Index,
1520- code: *std.ArrayListUnmanaged(u8),
1521+ atom_index: u32,
1522+ w: *std.Io.Writer,
1523 debug_output: link.File.DebugInfoOutput,
1524-) codegen.CodeGenError!void {
1525+) (codegen.CodeGenError || std.Io.Writer.Error)!void {
1526+ _ = atom_index;
1527 const zcu = pt.zcu;
1528 const func = zcu.funcInfo(func_index);
1529 const nav = func.owner_nav;
1530@@ -393,7 +395,7 @@ pub fn emit(
1531 .debug_output = debug_output,
1532 .target = &mod.resolved_target.result,
1533 .src_loc = src_loc,
1534- .code = code,
1535+ .w = w,
1536 .prev_di_pc = 0,
1537 .prev_di_line = func.lbrace_line,
1538 .prev_di_column = func.lbrace_column,
1539diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
1540index b5760c95ac2b54f5e03c32e3d61a4c32e01f3a46..1827b50d61a06e05f525461fb8261922f36920bf 100644
1541--- a/src/arch/x86_64/CodeGen.zig
1542+++ b/src/arch/x86_64/CodeGen.zig
1543@@ -550,9 +550,9 @@ pub const MCValue = union(enum) {
1544 @tagName(pl.reg),
1545 }),
1546 .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
1547- .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }),
1548- .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }),
1549- .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }),
1550+ .indirect_load_frame => |pl| try w.print("[[{f} + 0x{x}]]", .{ pl.index, pl.off }),
1551+ .load_frame => |pl| try w.print("[{f} + 0x{x}]", .{ pl.index, pl.off }),
1552+ .lea_frame => |pl| try w.print("{f} + 0x{x}", .{ pl.index, pl.off }),
1553 .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}),
1554 .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}),
1555 .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}),
1556@@ -561,10 +561,10 @@ pub const MCValue = union(enum) {
1557 .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }),
1558 .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}),
1559 .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}),
1560- .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{
1561+ .elementwise_args => |pl| try w.print("elementwise:{d}:[{f} + 0x{x}]", .{
1562 pl.regs, pl.frame_index, pl.frame_off,
1563 }),
1564- .reserved_frame => |pl| try w.print("(dead:{})", .{pl}),
1565+ .reserved_frame => |pl| try w.print("(dead:{f})", .{pl}),
1566 .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}),
1567 }
1568 }
1569@@ -1038,7 +1038,8 @@ pub fn generateLazy(
1570 pt: Zcu.PerThread,
1571 src_loc: Zcu.LazySrcLoc,
1572 lazy_sym: link.File.LazySymbol,
1573- code: *std.ArrayListUnmanaged(u8),
1574+ atom_index: u32,
1575+ w: *std.Io.Writer,
1576 debug_output: link.File.DebugInfoOutput,
1577 ) codegen.CodeGenError!void {
1578 const gpa = pt.zcu.gpa;
1579@@ -1081,7 +1082,7 @@ pub fn generateLazy(
1580 else => |e| return e,
1581 };
1582
1583- try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, code, debug_output);
1584+ try function.getTmpMir().emitLazy(bin_file, pt, src_loc, lazy_sym, atom_index, w, debug_output);
1585 }
1586
1587 const FormatNavData = struct {
1588@@ -2022,7 +2023,7 @@ fn gen(
1589 .{},
1590 );
1591 self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } };
1592- tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index });
1593+ tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index });
1594 },
1595 else => unreachable,
1596 }
1597diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
1598index 84bb325322ac20c100da1e85c715a99f7e251deb..c2b38d8e6d45b3439d2f8bb6cf9e518ac17784cc 100644
1599--- a/src/arch/x86_64/Emit.zig
1600+++ b/src/arch/x86_64/Emit.zig
1601@@ -6,7 +6,7 @@ pt: Zcu.PerThread,
1602 pic: bool,
1603 atom_index: u32,
1604 debug_output: link.File.DebugInfoOutput,
1605-code: *std.ArrayListUnmanaged(u8),
1606+w: *std.Io.Writer,
1607
1608 prev_di_loc: Loc,
1609 /// Relative to the beginning of `code`.
1610@@ -18,7 +18,8 @@ table_relocs: std.ArrayListUnmanaged(TableReloc),
1611
1612 pub const Error = Lower.Error || error{
1613 EmitFail,
1614-} || link.File.UpdateDebugInfoError;
1615+ NotFile,
1616+} || std.posix.MMapError || std.posix.MRemapError || link.File.UpdateDebugInfoError;
1617
1618 pub fn emitMir(emit: *Emit) Error!void {
1619 const comp = emit.bin_file.comp;
1620@@ -29,12 +30,12 @@ pub fn emitMir(emit: *Emit) Error!void {
1621 var local_index: usize = 0;
1622 for (0..emit.lower.mir.instructions.len) |mir_i| {
1623 const mir_index: Mir.Inst.Index = @intCast(mir_i);
1624- emit.code_offset_mapping.items[mir_index] = @intCast(emit.code.items.len);
1625+ emit.code_offset_mapping.items[mir_index] = @intCast(emit.w.end);
1626 const lowered = try emit.lower.lowerMir(mir_index);
1627 var lowered_relocs = lowered.relocs;
1628 lowered_inst: for (lowered.insts, 0..) |lowered_inst, lowered_index| {
1629 if (lowered_inst.prefix == .directive) {
1630- const start_offset: u32 = @intCast(emit.code.items.len);
1631+ const start_offset: u32 = @intCast(emit.w.end);
1632 switch (emit.debug_output) {
1633 .dwarf => |dwarf| switch (lowered_inst.encoding.mnemonic) {
1634 .@".cfi_def_cfa" => try dwarf.genDebugFrame(start_offset, .{ .def_cfa = .{
1635@@ -164,6 +165,8 @@ pub fn emitMir(emit: *Emit) Error!void {
1636 .index = if (emit.bin_file.cast(.elf)) |elf_file|
1637 elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, emit.pt, lazy_sym) catch |err|
1638 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
1639+ else if (emit.bin_file.cast(.elf2)) |elf|
1640+ @intFromEnum(try elf.lazySymbol(lazy_sym))
1641 else if (emit.bin_file.cast(.macho)) |macho_file|
1642 macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, emit.pt, lazy_sym) catch |err|
1643 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
1644@@ -180,12 +183,15 @@ pub fn emitMir(emit: *Emit) Error!void {
1645 .extern_func => |extern_func| .{
1646 .index = if (emit.bin_file.cast(.elf)) |elf_file|
1647 try elf_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
1648- else if (emit.bin_file.cast(.macho)) |macho_file|
1649+ else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
1650+ .name = extern_func.toSlice(&emit.lower.mir).?,
1651+ .type = .FUNC,
1652+ })) else if (emit.bin_file.cast(.macho)) |macho_file|
1653 try macho_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, null)
1654 else if (emit.bin_file.cast(.coff)) |coff_file|
1655 try coff_file.getGlobalSymbol(extern_func.toSlice(&emit.lower.mir).?, "compiler_rt")
1656 else
1657- return emit.fail("external symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
1658+ return emit.fail("external symbol unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
1659 .is_extern = true,
1660 .type = .symbol,
1661 },
1662@@ -205,7 +211,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1663 },
1664 else => {},
1665 }
1666- if (emit.bin_file.cast(.elf)) |_| {
1667+ if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
1668 if (!emit.pic) switch (lowered_inst.encoding.mnemonic) {
1669 .lea => try emit.encodeInst(try .new(.none, .mov, &.{
1670 lowered_inst.ops[0],
1671@@ -315,7 +321,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1672 },
1673 .branch, .tls => unreachable,
1674 .tlv => {
1675- if (emit.bin_file.cast(.elf)) |elf_file| {
1676+ if (emit.bin_file.cast(.elf) != null or emit.bin_file.cast(.elf2) != null) {
1677 // TODO handle extern TLS vars, i.e., emit GD model
1678 if (emit.pic) switch (lowered_inst.encoding.mnemonic) {
1679 .lea, .mov => {
1680@@ -337,7 +343,12 @@ pub fn emitMir(emit: *Emit) Error!void {
1681 }, emit.lower.target), &.{.{
1682 .op_index = 0,
1683 .target = .{
1684- .index = try elf_file.getGlobalSymbol("__tls_get_addr", null),
1685+ .index = if (emit.bin_file.cast(.elf)) |elf_file|
1686+ try elf_file.getGlobalSymbol("__tls_get_addr", null)
1687+ else if (emit.bin_file.cast(.elf2)) |elf| @intFromEnum(try elf.globalSymbol(.{
1688+ .name = "__tls_get_addr",
1689+ .type = .FUNC,
1690+ })) else unreachable,
1691 .is_extern = true,
1692 .type = .branch,
1693 },
1694@@ -441,7 +452,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1695 log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
1696 emit.prev_di_loc.line, emit.prev_di_loc.column,
1697 });
1698- try dwarf.enterBlock(emit.code.items.len);
1699+ try dwarf.enterBlock(emit.w.end);
1700 },
1701 .none => {},
1702 },
1703@@ -450,7 +461,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1704 log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
1705 emit.prev_di_loc.line, emit.prev_di_loc.column,
1706 });
1707- try dwarf.leaveBlock(emit.code.items.len);
1708+ try dwarf.leaveBlock(emit.w.end);
1709 },
1710 .none => {},
1711 },
1712@@ -459,7 +470,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1713 log.debug("mirDbgEnterInline (line={d}, col={d})", .{
1714 emit.prev_di_loc.line, emit.prev_di_loc.column,
1715 });
1716- try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
1717+ try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.w.end, emit.prev_di_loc.line, emit.prev_di_loc.column);
1718 },
1719 .none => {},
1720 },
1721@@ -468,7 +479,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1722 log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
1723 emit.prev_di_loc.line, emit.prev_di_loc.column,
1724 });
1725- try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.code.items.len);
1726+ try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.w.end);
1727 },
1728 .none => {},
1729 },
1730@@ -634,7 +645,7 @@ pub fn emitMir(emit: *Emit) Error!void {
1731 for (emit.relocs.items) |reloc| {
1732 const target = emit.code_offset_mapping.items[reloc.target];
1733 const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.inst_offset + reloc.inst_length)) + reloc.target_offset;
1734- const inst_bytes = emit.code.items[reloc.inst_offset..][0..reloc.inst_length];
1735+ const inst_bytes = emit.w.buffered()[reloc.inst_offset..][0..reloc.inst_length];
1736 switch (reloc.source_length) {
1737 else => unreachable,
1738 inline 1, 4 => |source_length| std.mem.writeInt(
1739@@ -646,12 +657,12 @@ pub fn emitMir(emit: *Emit) Error!void {
1740 }
1741 }
1742 if (emit.lower.mir.table.len > 0) {
1743+ const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
1744+ var table_offset = std.mem.alignForward(u32, @intCast(emit.w.end), ptr_size);
1745 if (emit.bin_file.cast(.elf)) |elf_file| {
1746 const zo = elf_file.zigObjectPtr().?;
1747 const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
1748
1749- const ptr_size = @divExact(emit.lower.target.ptrBitWidth(), 8);
1750- var table_offset = std.mem.alignForward(u32, @intCast(emit.code.items.len), ptr_size);
1751 for (emit.table_relocs.items) |table_reloc| try atom.addReloc(gpa, .{
1752 .r_offset = table_reloc.source_offset,
1753 .r_info = @as(u64, emit.atom_index) << 32 | @intFromEnum(std.elf.R_X86_64.@"32"),
1754@@ -665,7 +676,26 @@ pub fn emitMir(emit: *Emit) Error!void {
1755 }, zo);
1756 table_offset += ptr_size;
1757 }
1758- try emit.code.appendNTimes(gpa, 0, table_offset - emit.code.items.len);
1759+ try emit.w.splatByteAll(0, table_offset - emit.w.end);
1760+ } else if (emit.bin_file.cast(.elf2)) |elf| {
1761+ for (emit.table_relocs.items) |table_reloc| try elf.addReloc(
1762+ @enumFromInt(emit.atom_index),
1763+ table_reloc.source_offset,
1764+ @enumFromInt(emit.atom_index),
1765+ @as(i64, table_offset) + table_reloc.target_offset,
1766+ .{ .x86_64 = .@"32" },
1767+ );
1768+ for (emit.lower.mir.table) |entry| {
1769+ try elf.addReloc(
1770+ @enumFromInt(emit.atom_index),
1771+ table_offset,
1772+ @enumFromInt(emit.atom_index),
1773+ emit.code_offset_mapping.items[entry],
1774+ .{ .x86_64 = .@"64" },
1775+ );
1776+ table_offset += ptr_size;
1777+ }
1778+ try emit.w.splatByteAll(0, table_offset - emit.w.end);
1779 } else unreachable;
1780 }
1781 }
1782@@ -696,16 +726,12 @@ const RelocInfo = struct {
1783 fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocInfo) Error!void {
1784 const comp = emit.bin_file.comp;
1785 const gpa = comp.gpa;
1786- const start_offset: u32 = @intCast(emit.code.items.len);
1787- {
1788- var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, emit.code);
1789- defer emit.code.* = aw.toArrayList();
1790- lowered_inst.encode(&aw.writer, .{}) catch |err| switch (err) {
1791- error.WriteFailed => return error.OutOfMemory,
1792- else => |e| return e,
1793- };
1794- }
1795- const end_offset: u32 = @intCast(emit.code.items.len);
1796+ const start_offset: u32 = @intCast(emit.w.end);
1797+ lowered_inst.encode(emit.w, .{}) catch |err| switch (err) {
1798+ error.WriteFailed => return error.OutOfMemory,
1799+ else => |e| return e,
1800+ };
1801+ const end_offset: u32 = @intCast(emit.w.end);
1802 for (reloc_info) |reloc| switch (reloc.target.type) {
1803 .inst => {
1804 const inst_length: u4 = @intCast(end_offset - start_offset);
1805@@ -769,7 +795,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
1806 .symbolnum = @intCast(reloc.target.index),
1807 },
1808 });
1809- } else if (emit.bin_file.cast(.coff)) |coff_file| {
1810+ } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
1811+ @enumFromInt(emit.atom_index),
1812+ end_offset - 4,
1813+ @enumFromInt(reloc.target.index),
1814+ reloc.off,
1815+ .{ .x86_64 = .@"32" },
1816+ ) else if (emit.bin_file.cast(.coff)) |coff_file| {
1817 const atom_index = coff_file.getAtomIndexForSymbol(
1818 .{ .sym_index = emit.atom_index, .file = null },
1819 ).?;
1820@@ -794,7 +826,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
1821 .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
1822 .r_addend = reloc.off - 4,
1823 }, zo);
1824- } else if (emit.bin_file.cast(.macho)) |macho_file| {
1825+ } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
1826+ @enumFromInt(emit.atom_index),
1827+ end_offset - 4,
1828+ @enumFromInt(reloc.target.index),
1829+ reloc.off - 4,
1830+ .{ .x86_64 = .PC32 },
1831+ ) else if (emit.bin_file.cast(.macho)) |macho_file| {
1832 const zo = macho_file.getZigObject().?;
1833 const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
1834 try atom.addReloc(macho_file, .{
1835@@ -849,7 +887,13 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
1836 .r_info = @as(u64, reloc.target.index) << 32 | @intFromEnum(r_type),
1837 .r_addend = reloc.off,
1838 }, zo);
1839- } else if (emit.bin_file.cast(.macho)) |macho_file| {
1840+ } else if (emit.bin_file.cast(.elf2)) |elf| try elf.addReloc(
1841+ @enumFromInt(emit.atom_index),
1842+ end_offset - 4,
1843+ @enumFromInt(reloc.target.index),
1844+ reloc.off,
1845+ .{ .x86_64 = .TPOFF32 },
1846+ ) else if (emit.bin_file.cast(.macho)) |macho_file| {
1847 const zo = macho_file.getZigObject().?;
1848 const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
1849 try atom.addReloc(macho_file, .{
1850@@ -908,7 +952,7 @@ const Loc = struct {
1851
1852 fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
1853 const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line);
1854- const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
1855+ const delta_pc: usize = emit.w.end - emit.prev_di_pc;
1856 log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
1857 switch (emit.debug_output) {
1858 .dwarf => |dwarf| {
1859@@ -916,7 +960,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
1860 if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column);
1861 try dwarf.advancePCAndLine(delta_line, delta_pc);
1862 emit.prev_di_loc = loc;
1863- emit.prev_di_pc = emit.code.items.len;
1864+ emit.prev_di_pc = emit.w.end;
1865 },
1866 .none => {},
1867 }
1868diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig
1869index e70018850d621aff1a360044014f1ea1e8516a44..caf41ffb392d07b8a455f09181c77ffb8a679d7e 100644
1870--- a/src/arch/x86_64/Mir.zig
1871+++ b/src/arch/x86_64/Mir.zig
1872@@ -1976,7 +1976,8 @@ pub fn emit(
1873 pt: Zcu.PerThread,
1874 src_loc: Zcu.LazySrcLoc,
1875 func_index: InternPool.Index,
1876- code: *std.ArrayListUnmanaged(u8),
1877+ atom_index: u32,
1878+ w: *std.Io.Writer,
1879 debug_output: link.File.DebugInfoOutput,
1880 ) codegen.CodeGenError!void {
1881 const zcu = pt.zcu;
1882@@ -1997,17 +1998,9 @@ pub fn emit(
1883 .bin_file = lf,
1884 .pt = pt,
1885 .pic = mod.pic,
1886- .atom_index = sym: {
1887- if (lf.cast(.elf)) |ef| break :sym try ef.zigObjectPtr().?.getOrCreateMetadataForNav(zcu, nav);
1888- if (lf.cast(.macho)) |mf| break :sym try mf.getZigObject().?.getOrCreateMetadataForNav(mf, nav);
1889- if (lf.cast(.coff)) |cf| {
1890- const atom = try cf.getOrCreateAtomForNav(nav);
1891- break :sym cf.getAtom(atom).getSymbolIndex().?;
1892- }
1893- unreachable;
1894- },
1895+ .atom_index = atom_index,
1896 .debug_output = debug_output,
1897- .code = code,
1898+ .w = w,
1899
1900 .prev_di_loc = .{
1901 .line = func.lbrace_line,
1902@@ -2037,7 +2030,8 @@ pub fn emitLazy(
1903 pt: Zcu.PerThread,
1904 src_loc: Zcu.LazySrcLoc,
1905 lazy_sym: link.File.LazySymbol,
1906- code: *std.ArrayListUnmanaged(u8),
1907+ atom_index: u32,
1908+ w: *std.Io.Writer,
1909 debug_output: link.File.DebugInfoOutput,
1910 ) codegen.CodeGenError!void {
1911 const zcu = pt.zcu;
1912@@ -2055,20 +2049,9 @@ pub fn emitLazy(
1913 .bin_file = lf,
1914 .pt = pt,
1915 .pic = mod.pic,
1916- .atom_index = sym: {
1917- if (lf.cast(.elf)) |ef| break :sym ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_sym) catch |err|
1918- return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
1919- if (lf.cast(.macho)) |mf| break :sym mf.getZigObject().?.getOrCreateMetadataForLazySymbol(mf, pt, lazy_sym) catch |err|
1920- return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
1921- if (lf.cast(.coff)) |cf| {
1922- const atom = cf.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
1923- return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
1924- break :sym cf.getAtom(atom).getSymbolIndex().?;
1925- }
1926- unreachable;
1927- },
1928+ .atom_index = atom_index,
1929 .debug_output = debug_output,
1930- .code = code,
1931+ .w = w,
1932
1933 .prev_di_loc = undefined,
1934 .prev_di_pc = undefined,
1935diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig
1936index 835093d11ab8d38f622252233101c1a3c9d8904d..05048109335e0256d91c5f350d7fa900ce08e402 100644
1937--- a/src/arch/x86_64/bits.zig
1938+++ b/src/arch/x86_64/bits.zig
1939@@ -727,6 +727,14 @@ pub const FrameIndex = enum(u32) {
1940 pub fn isNamed(fi: FrameIndex) bool {
1941 return @intFromEnum(fi) < named_count;
1942 }
1943+
1944+ pub fn format(fi: FrameIndex, writer: *std.Io.Writer) std.Io.Writer.Error!void {
1945+ if (fi.isNamed()) {
1946+ try writer.print("FrameIndex.{t}", .{fi});
1947+ } else {
1948+ try writer.print("FrameIndex({d})", .{@intFromEnum(fi)});
1949+ }
1950+ }
1951 };
1952
1953 pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 };
1954diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig
1955index 03ff7134f62d04025081f06b50248545433130ec..1d9c67175ef204933a5463393cad189d0e59e7fa 100644
1956--- a/src/arch/x86_64/encoder.zig
1957+++ b/src/arch/x86_64/encoder.zig
1958@@ -259,7 +259,7 @@ pub const Instruction = struct {
1959 switch (sib.base) {
1960 .none => any = false,
1961 .reg => |reg| try w.print("{s}", .{@tagName(reg)}),
1962- .frame => |frame_index| try w.print("{}", .{frame_index}),
1963+ .frame => |frame_index| try w.print("{f}", .{frame_index}),
1964 .table => try w.print("Table", .{}),
1965 .rip_inst => |inst_index| try w.print("RipInst({d})", .{inst_index}),
1966 .nav => |nav| try w.print("Nav({d})", .{@intFromEnum(nav)}),
1967diff --git a/src/codegen.zig b/src/codegen.zig
1968index 56e6e7c99fb3bd8208ce92e912c03f3d2404f2c1..4cbf3f56169ec9227f961a72720898cc23df3f96 100644
1969--- a/src/codegen.zig
1970+++ b/src/codegen.zig
1971@@ -6,7 +6,6 @@ const link = @import("link.zig");
1972 const log = std.log.scoped(.codegen);
1973 const mem = std.mem;
1974 const math = std.math;
1975-const ArrayList = std.ArrayList;
1976 const target_util = @import("target.zig");
1977 const trace = @import("tracy.zig").trace;
1978
1979@@ -179,10 +178,11 @@ pub fn emitFunction(
1980 pt: Zcu.PerThread,
1981 src_loc: Zcu.LazySrcLoc,
1982 func_index: InternPool.Index,
1983+ atom_index: u32,
1984 any_mir: *const AnyMir,
1985- code: *ArrayList(u8),
1986+ w: *std.Io.Writer,
1987 debug_output: link.File.DebugInfoOutput,
1988-) CodeGenError!void {
1989+) (CodeGenError || std.Io.Writer.Error)!void {
1990 const zcu = pt.zcu;
1991 const func = zcu.funcInfo(func_index);
1992 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result;
1993@@ -195,7 +195,7 @@ pub fn emitFunction(
1994 => |backend| {
1995 dev.check(devFeatureForBackend(backend));
1996 const mir = &@field(any_mir, AnyMir.tag(backend));
1997- return mir.emit(lf, pt, src_loc, func_index, code, debug_output);
1998+ return mir.emit(lf, pt, src_loc, func_index, atom_index, w, debug_output);
1999 },
2000 }
2001 }
2002@@ -205,9 +205,10 @@ pub fn generateLazyFunction(
2003 pt: Zcu.PerThread,
2004 src_loc: Zcu.LazySrcLoc,
2005 lazy_sym: link.File.LazySymbol,
2006- code: *ArrayList(u8),
2007+ atom_index: u32,
2008+ w: *std.Io.Writer,
2009 debug_output: link.File.DebugInfoOutput,
2010-) CodeGenError!void {
2011+) (CodeGenError || std.Io.Writer.Error)!void {
2012 const zcu = pt.zcu;
2013 const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index|
2014 &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result
2015@@ -217,19 +218,11 @@ pub fn generateLazyFunction(
2016 else => unreachable,
2017 inline .stage2_riscv64, .stage2_x86_64 => |backend| {
2018 dev.check(devFeatureForBackend(backend));
2019- return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output);
2020+ return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, atom_index, w, debug_output);
2021 },
2022 }
2023 }
2024
2025-fn writeFloat(comptime F: type, f: F, target: *const std.Target, endian: std.builtin.Endian, code: []u8) void {
2026- _ = target;
2027- const bits = @typeInfo(F).float.bits;
2028- const Int = @Type(.{ .int = .{ .signedness = .unsigned, .bits = bits } });
2029- const int: Int = @bitCast(f);
2030- mem.writeInt(Int, code[0..@divExact(bits, 8)], int, endian);
2031-}
2032-
2033 pub fn generateLazySymbol(
2034 bin_file: *link.File,
2035 pt: Zcu.PerThread,
2036@@ -237,17 +230,14 @@ pub fn generateLazySymbol(
2037 lazy_sym: link.File.LazySymbol,
2038 // TODO don't use an "out" parameter like this; put it in the result instead
2039 alignment: *Alignment,
2040- code: *ArrayList(u8),
2041+ w: *std.Io.Writer,
2042 debug_output: link.File.DebugInfoOutput,
2043 reloc_parent: link.File.RelocInfo.Parent,
2044-) CodeGenError!void {
2045- _ = reloc_parent;
2046-
2047+) (CodeGenError || std.Io.Writer.Error)!void {
2048 const tracy = trace(@src());
2049 defer tracy.end();
2050
2051 const comp = bin_file.comp;
2052- const gpa = comp.gpa;
2053 const zcu = pt.zcu;
2054 const ip = &zcu.intern_pool;
2055 const target = &comp.root_mod.resolved_target.result;
2056@@ -260,37 +250,36 @@ pub fn generateLazySymbol(
2057
2058 if (lazy_sym.kind == .code) {
2059 alignment.* = target_util.defaultFunctionAlignment(target);
2060- return generateLazyFunction(bin_file, pt, src_loc, lazy_sym, code, debug_output);
2061+ return generateLazyFunction(bin_file, pt, src_loc, lazy_sym, reloc_parent.atom_index, w, debug_output);
2062 }
2063
2064 if (lazy_sym.ty == .anyerror_type) {
2065 alignment.* = .@"4";
2066 const err_names = ip.global_error_set.getNamesFromMainThread();
2067- var offset_index: u32 = @intCast(code.items.len);
2068- var string_index: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));
2069- try code.resize(gpa, offset_index + string_index);
2070- mem.writeInt(u32, code.items[offset_index..][0..4], @intCast(err_names.len), endian);
2071+ const strings_start: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0)));
2072+ var string_index = strings_start;
2073+ try w.rebase(w.end, string_index);
2074+ w.writeInt(u32, @intCast(err_names.len), endian) catch unreachable;
2075 if (err_names.len == 0) return;
2076- offset_index += 4;
2077 for (err_names) |err_name_nts| {
2078- const err_name = err_name_nts.toSlice(ip);
2079- mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
2080- offset_index += 4;
2081- try code.ensureUnusedCapacity(gpa, err_name.len + 1);
2082- code.appendSliceAssumeCapacity(err_name);
2083- code.appendAssumeCapacity(0);
2084- string_index += @intCast(err_name.len + 1);
2085+ w.writeInt(u32, string_index, endian) catch unreachable;
2086+ string_index += @intCast(err_name_nts.toSlice(ip).len + 1);
2087+ }
2088+ w.writeInt(u32, string_index, endian) catch unreachable;
2089+ try w.rebase(w.end, string_index - strings_start);
2090+ for (err_names) |err_name_nts| {
2091+ w.writeAll(err_name_nts.toSlice(ip)) catch unreachable;
2092+ w.writeByte(0) catch unreachable;
2093 }
2094- mem.writeInt(u32, code.items[offset_index..][0..4], string_index, endian);
2095 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") {
2096 alignment.* = .@"1";
2097 const enum_ty = Type.fromInterned(lazy_sym.ty);
2098 const tag_names = enum_ty.enumFields(zcu);
2099 for (0..tag_names.len) |tag_index| {
2100 const tag_name = tag_names.get(ip)[tag_index].toSlice(ip);
2101- try code.ensureUnusedCapacity(gpa, tag_name.len + 1);
2102- code.appendSliceAssumeCapacity(tag_name);
2103- code.appendAssumeCapacity(0);
2104+ try w.rebase(w.end, tag_name.len + 1);
2105+ w.writeAll(tag_name) catch unreachable;
2106+ w.writeByte(0) catch unreachable;
2107 }
2108 } else {
2109 return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {f}", .{
2110@@ -312,14 +301,13 @@ pub fn generateSymbol(
2111 pt: Zcu.PerThread,
2112 src_loc: Zcu.LazySrcLoc,
2113 val: Value,
2114- code: *ArrayList(u8),
2115+ w: *std.Io.Writer,
2116 reloc_parent: link.File.RelocInfo.Parent,
2117-) GenerateSymbolError!void {
2118+) (GenerateSymbolError || std.Io.Writer.Error)!void {
2119 const tracy = trace(@src());
2120 defer tracy.end();
2121
2122 const zcu = pt.zcu;
2123- const gpa = zcu.gpa;
2124 const ip = &zcu.intern_pool;
2125 const ty = val.typeOf(zcu);
2126
2127@@ -330,7 +318,7 @@ pub fn generateSymbol(
2128
2129 if (val.isUndef(zcu)) {
2130 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
2131- try code.appendNTimes(gpa, 0xaa, abi_size);
2132+ try w.splatByteAll(0xaa, abi_size);
2133 return;
2134 }
2135
2136@@ -360,7 +348,7 @@ pub fn generateSymbol(
2137 .null => unreachable, // non-runtime value
2138 .@"unreachable" => unreachable, // non-runtime value
2139 .empty_tuple => return,
2140- .false, .true => try code.append(gpa, switch (simple_value) {
2141+ .false, .true => try w.writeByte(switch (simple_value) {
2142 .false => 0,
2143 .true => 1,
2144 else => unreachable,
2145@@ -376,11 +364,11 @@ pub fn generateSymbol(
2146 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
2147 var space: Value.BigIntSpace = undefined;
2148 const int_val = val.toBigInt(&space, zcu);
2149- int_val.writeTwosComplement(try code.addManyAsSlice(gpa, abi_size), endian);
2150+ int_val.writeTwosComplement(try w.writableSlice(abi_size), endian);
2151 },
2152 .err => |err| {
2153 const int = try pt.getErrorValue(err.name);
2154- mem.writeInt(u16, try code.addManyAsArray(gpa, 2), @intCast(int), endian);
2155+ try w.writeInt(u16, @intCast(int), endian);
2156 },
2157 .error_union => |error_union| {
2158 const payload_ty = ty.errorUnionPayload(zcu);
2159@@ -390,7 +378,7 @@ pub fn generateSymbol(
2160 };
2161
2162 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2163- mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
2164+ try w.writeInt(u16, err_val, endian);
2165 return;
2166 }
2167
2168@@ -400,63 +388,63 @@ pub fn generateSymbol(
2169
2170 // error value first when its type is larger than the error union's payload
2171 if (error_align.order(payload_align) == .gt) {
2172- mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
2173+ try w.writeInt(u16, err_val, endian);
2174 }
2175
2176 // emit payload part of the error union
2177 {
2178- const begin = code.items.len;
2179+ const begin = w.end;
2180 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(switch (error_union.val) {
2181 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }),
2182 .payload => |payload| payload,
2183- }), code, reloc_parent);
2184- const unpadded_end = code.items.len - begin;
2185+ }), w, reloc_parent);
2186+ const unpadded_end = w.end - begin;
2187 const padded_end = abi_align.forward(unpadded_end);
2188 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
2189
2190 if (padding > 0) {
2191- try code.appendNTimes(gpa, 0, padding);
2192+ try w.splatByteAll(0, padding);
2193 }
2194 }
2195
2196 // Payload size is larger than error set, so emit our error set last
2197 if (error_align.compare(.lte, payload_align)) {
2198- const begin = code.items.len;
2199- mem.writeInt(u16, try code.addManyAsArray(gpa, 2), err_val, endian);
2200- const unpadded_end = code.items.len - begin;
2201+ const begin = w.end;
2202+ try w.writeInt(u16, err_val, endian);
2203+ const unpadded_end = w.end - begin;
2204 const padded_end = abi_align.forward(unpadded_end);
2205 const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow;
2206
2207 if (padding > 0) {
2208- try code.appendNTimes(gpa, 0, padding);
2209+ try w.splatByteAll(0, padding);
2210 }
2211 }
2212 },
2213 .enum_tag => |enum_tag| {
2214 const int_tag_ty = ty.intTagType(zcu);
2215- try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), code, reloc_parent);
2216+ try generateSymbol(bin_file, pt, src_loc, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent);
2217 },
2218 .float => |float| storage: switch (float.storage) {
2219- .f16 => |f16_val| writeFloat(f16, f16_val, target, endian, try code.addManyAsArray(gpa, 2)),
2220- .f32 => |f32_val| writeFloat(f32, f32_val, target, endian, try code.addManyAsArray(gpa, 4)),
2221- .f64 => |f64_val| writeFloat(f64, f64_val, target, endian, try code.addManyAsArray(gpa, 8)),
2222+ .f16 => |f16_val| try w.writeInt(u16, @bitCast(f16_val), endian),
2223+ .f32 => |f32_val| try w.writeInt(u32, @bitCast(f32_val), endian),
2224+ .f64 => |f64_val| try w.writeInt(u64, @bitCast(f64_val), endian),
2225 .f80 => |f80_val| {
2226- writeFloat(f80, f80_val, target, endian, try code.addManyAsArray(gpa, 10));
2227+ try w.writeInt(u80, @bitCast(f80_val), endian);
2228 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
2229- try code.appendNTimes(gpa, 0, abi_size - 10);
2230+ try w.splatByteAll(0, abi_size - 10);
2231 },
2232 .f128 => |f128_val| switch (Type.fromInterned(float.ty).floatBits(target)) {
2233 else => unreachable,
2234 16 => continue :storage .{ .f16 = @floatCast(f128_val) },
2235 32 => continue :storage .{ .f32 = @floatCast(f128_val) },
2236 64 => continue :storage .{ .f64 = @floatCast(f128_val) },
2237- 128 => writeFloat(f128, f128_val, target, endian, try code.addManyAsArray(gpa, 16)),
2238+ 128 => try w.writeInt(u128, @bitCast(f128_val), endian),
2239 },
2240 },
2241- .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), code, reloc_parent, 0),
2242+ .ptr => try lowerPtr(bin_file, pt, src_loc, val.toIntern(), w, reloc_parent, 0),
2243 .slice => |slice| {
2244- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), code, reloc_parent);
2245- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), code, reloc_parent);
2246+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.ptr), w, reloc_parent);
2247+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(slice.len), w, reloc_parent);
2248 },
2249 .opt => {
2250 const payload_type = ty.optionalChild(zcu);
2251@@ -465,9 +453,9 @@ pub fn generateSymbol(
2252
2253 if (ty.optionalReprIsPayload(zcu)) {
2254 if (payload_val) |value| {
2255- try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
2256+ try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent);
2257 } else {
2258- try code.appendNTimes(gpa, 0, abi_size);
2259+ try w.splatByteAll(0, abi_size);
2260 }
2261 } else {
2262 const padding = abi_size - (math.cast(usize, payload_type.abiSize(zcu)) orelse return error.Overflow) - 1;
2263@@ -475,15 +463,15 @@ pub fn generateSymbol(
2264 const value = payload_val orelse Value.fromInterned(try pt.intern(.{
2265 .undef = payload_type.toIntern(),
2266 }));
2267- try generateSymbol(bin_file, pt, src_loc, value, code, reloc_parent);
2268+ try generateSymbol(bin_file, pt, src_loc, value, w, reloc_parent);
2269 }
2270- try code.append(gpa, @intFromBool(payload_val != null));
2271- try code.appendNTimes(gpa, 0, padding);
2272+ try w.writeByte(@intFromBool(payload_val != null));
2273+ try w.splatByteAll(0, padding);
2274 }
2275 },
2276 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {
2277 .array_type => |array_type| switch (aggregate.storage) {
2278- .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(array_type.lenIncludingSentinel(), ip)),
2279+ .bytes => |bytes| try w.writeAll(bytes.toSlice(array_type.lenIncludingSentinel(), ip)),
2280 .elems, .repeated_elem => {
2281 var index: u64 = 0;
2282 while (index < array_type.lenIncludingSentinel()) : (index += 1) {
2283@@ -494,14 +482,14 @@ pub fn generateSymbol(
2284 elem
2285 else
2286 array_type.sentinel,
2287- }), code, reloc_parent);
2288+ }), w, reloc_parent);
2289 }
2290 },
2291 },
2292 .vector_type => |vector_type| {
2293 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
2294 if (vector_type.child == .bool_type) {
2295- const bytes = try code.addManyAsSlice(gpa, abi_size);
2296+ const bytes = try w.writableSlice(abi_size);
2297 @memset(bytes, 0xaa);
2298 var index: usize = 0;
2299 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
2300@@ -540,7 +528,7 @@ pub fn generateSymbol(
2301 }
2302 } else {
2303 switch (aggregate.storage) {
2304- .bytes => |bytes| try code.appendSlice(gpa, bytes.toSlice(vector_type.len, ip)),
2305+ .bytes => |bytes| try w.writeAll(bytes.toSlice(vector_type.len, ip)),
2306 .elems, .repeated_elem => {
2307 var index: u64 = 0;
2308 while (index < vector_type.len) : (index += 1) {
2309@@ -550,7 +538,7 @@ pub fn generateSymbol(
2310 math.cast(usize, index) orelse return error.Overflow
2311 ],
2312 .repeated_elem => |elem| elem,
2313- }), code, reloc_parent);
2314+ }), w, reloc_parent);
2315 }
2316 },
2317 }
2318@@ -558,11 +546,11 @@ pub fn generateSymbol(
2319 const padding = abi_size -
2320 (math.cast(usize, Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len) orelse
2321 return error.Overflow);
2322- if (padding > 0) try code.appendNTimes(gpa, 0, padding);
2323+ if (padding > 0) try w.splatByteAll(0, padding);
2324 }
2325 },
2326 .tuple_type => |tuple| {
2327- const struct_begin = code.items.len;
2328+ const struct_begin = w.end;
2329 for (
2330 tuple.types.get(ip),
2331 tuple.values.get(ip),
2332@@ -580,8 +568,8 @@ pub fn generateSymbol(
2333 .repeated_elem => |elem| elem,
2334 };
2335
2336- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
2337- const unpadded_field_end = code.items.len - struct_begin;
2338+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
2339+ const unpadded_field_end = w.end - struct_begin;
2340
2341 // Pad struct members if required
2342 const padded_field_end = ty.structFieldOffset(index + 1, zcu);
2343@@ -589,7 +577,7 @@ pub fn generateSymbol(
2344 return error.Overflow;
2345
2346 if (padding > 0) {
2347- try code.appendNTimes(gpa, 0, padding);
2348+ try w.splatByteAll(0, padding);
2349 }
2350 }
2351 },
2352@@ -598,8 +586,9 @@ pub fn generateSymbol(
2353 switch (struct_type.layout) {
2354 .@"packed" => {
2355 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
2356- const current_pos = code.items.len;
2357- try code.appendNTimes(gpa, 0, abi_size);
2358+ const start = w.end;
2359+ const buffer = try w.writableSlice(abi_size);
2360+ @memset(buffer, 0);
2361 var bits: u16 = 0;
2362
2363 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
2364@@ -619,22 +608,20 @@ pub fn generateSymbol(
2365 error.DivisionByZero => unreachable,
2366 error.UnexpectedRemainder => return error.RelocationNotByteAligned,
2367 };
2368- code.items.len = current_pos + field_offset;
2369- // TODO: code.lockPointers();
2370+ w.end = start + field_offset;
2371 defer {
2372- assert(code.items.len == current_pos + field_offset + @divExact(target.ptrBitWidth(), 8));
2373- // TODO: code.unlockPointers();
2374- code.items.len = current_pos + abi_size;
2375+ assert(w.end == start + field_offset + @divExact(target.ptrBitWidth(), 8));
2376+ w.end = start + abi_size;
2377 }
2378- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
2379+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
2380 } else {
2381- Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;
2382+ Value.fromInterned(field_val).writeToPackedMemory(.fromInterned(field_ty), pt, buffer, bits) catch unreachable;
2383 }
2384 bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));
2385 }
2386 },
2387 .auto, .@"extern" => {
2388- const struct_begin = code.items.len;
2389+ const struct_begin = w.end;
2390 const field_types = struct_type.field_types.get(ip);
2391 const offsets = struct_type.offsets.get(ip);
2392
2393@@ -654,11 +641,11 @@ pub fn generateSymbol(
2394
2395 const padding = math.cast(
2396 usize,
2397- offsets[field_index] - (code.items.len - struct_begin),
2398+ offsets[field_index] - (w.end - struct_begin),
2399 ) orelse return error.Overflow;
2400- if (padding > 0) try code.appendNTimes(gpa, 0, padding);
2401+ if (padding > 0) try w.splatByteAll(0, padding);
2402
2403- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
2404+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
2405 }
2406
2407 const size = struct_type.sizeUnordered(ip);
2408@@ -666,10 +653,9 @@ pub fn generateSymbol(
2409
2410 const padding = math.cast(
2411 usize,
2412- std.mem.alignForward(u64, size, @max(alignment, 1)) -
2413- (code.items.len - struct_begin),
2414+ std.mem.alignForward(u64, size, @max(alignment, 1)) - (w.end - struct_begin),
2415 ) orelse return error.Overflow;
2416- if (padding > 0) try code.appendNTimes(gpa, 0, padding);
2417+ if (padding > 0) try w.splatByteAll(0, padding);
2418 },
2419 }
2420 },
2421@@ -679,12 +665,12 @@ pub fn generateSymbol(
2422 const layout = ty.unionGetLayout(zcu);
2423
2424 if (layout.payload_size == 0) {
2425- return generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
2426+ return generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
2427 }
2428
2429 // Check if we should store the tag first.
2430 if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) {
2431- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
2432+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
2433 }
2434
2435 const union_obj = zcu.typeToUnion(ty).?;
2436@@ -692,24 +678,24 @@ pub fn generateSymbol(
2437 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
2438 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
2439 if (!field_ty.hasRuntimeBits(zcu)) {
2440- try code.appendNTimes(gpa, 0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
2441+ try w.splatByteAll(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
2442 } else {
2443- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
2444+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent);
2445
2446 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(zcu)) orelse return error.Overflow;
2447 if (padding > 0) {
2448- try code.appendNTimes(gpa, 0, padding);
2449+ try w.splatByteAll(0, padding);
2450 }
2451 }
2452 } else {
2453- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), code, reloc_parent);
2454+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.val), w, reloc_parent);
2455 }
2456
2457 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
2458- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), code, reloc_parent);
2459+ try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(un.tag), w, reloc_parent);
2460
2461 if (layout.padding > 0) {
2462- try code.appendNTimes(gpa, 0, layout.padding);
2463+ try w.splatByteAll(0, layout.padding);
2464 }
2465 }
2466 },
2467@@ -722,30 +708,30 @@ fn lowerPtr(
2468 pt: Zcu.PerThread,
2469 src_loc: Zcu.LazySrcLoc,
2470 ptr_val: InternPool.Index,
2471- code: *ArrayList(u8),
2472+ w: *std.Io.Writer,
2473 reloc_parent: link.File.RelocInfo.Parent,
2474 prev_offset: u64,
2475-) GenerateSymbolError!void {
2476+) (GenerateSymbolError || std.Io.Writer.Error)!void {
2477 const zcu = pt.zcu;
2478 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr;
2479 const offset: u64 = prev_offset + ptr.byte_offset;
2480 return switch (ptr.base_addr) {
2481- .nav => |nav| try lowerNavRef(bin_file, pt, nav, code, reloc_parent, offset),
2482- .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, code, reloc_parent, offset),
2483- .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), code, reloc_parent),
2484+ .nav => |nav| try lowerNavRef(bin_file, pt, nav, w, reloc_parent, offset),
2485+ .uav => |uav| try lowerUavRef(bin_file, pt, src_loc, uav, w, reloc_parent, offset),
2486+ .int => try generateSymbol(bin_file, pt, src_loc, try pt.intValue(Type.usize, offset), w, reloc_parent),
2487 .eu_payload => |eu_ptr| try lowerPtr(
2488 bin_file,
2489 pt,
2490 src_loc,
2491 eu_ptr,
2492- code,
2493+ w,
2494 reloc_parent,
2495 offset + errUnionPayloadOffset(
2496 Value.fromInterned(eu_ptr).typeOf(zcu).childType(zcu).errorUnionPayload(zcu),
2497 zcu,
2498 ),
2499 ),
2500- .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, src_loc, opt_ptr, code, reloc_parent, offset),
2501+ .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, src_loc, opt_ptr, w, reloc_parent, offset),
2502 .field => |field| {
2503 const base_ptr = Value.fromInterned(field.base);
2504 const base_ty = base_ptr.typeOf(zcu).childType(zcu);
2505@@ -764,7 +750,7 @@ fn lowerPtr(
2506 },
2507 else => unreachable,
2508 };
2509- return lowerPtr(bin_file, pt, src_loc, field.base, code, reloc_parent, offset + field_off);
2510+ return lowerPtr(bin_file, pt, src_loc, field.base, w, reloc_parent, offset + field_off);
2511 },
2512 .arr_elem, .comptime_field, .comptime_alloc => unreachable,
2513 };
2514@@ -775,12 +761,11 @@ fn lowerUavRef(
2515 pt: Zcu.PerThread,
2516 src_loc: Zcu.LazySrcLoc,
2517 uav: InternPool.Key.Ptr.BaseAddr.Uav,
2518- code: *ArrayList(u8),
2519+ w: *std.Io.Writer,
2520 reloc_parent: link.File.RelocInfo.Parent,
2521 offset: u64,
2522-) GenerateSymbolError!void {
2523+) (GenerateSymbolError || std.Io.Writer.Error)!void {
2524 const zcu = pt.zcu;
2525- const gpa = zcu.gpa;
2526 const ip = &zcu.intern_pool;
2527 const comp = lf.comp;
2528 const target = &comp.root_mod.resolved_target.result;
2529@@ -790,10 +775,9 @@ fn lowerUavRef(
2530 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn";
2531
2532 log.debug("lowerUavRef: ty = {f}", .{uav_ty.fmt(pt)});
2533- try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
2534
2535 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) {
2536- code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
2537+ try w.splatByteAll(0xaa, ptr_width_bytes);
2538 return;
2539 }
2540
2541@@ -804,29 +788,32 @@ fn lowerUavRef(
2542 dev.check(link.File.Tag.wasm.devFeature());
2543 const wasm = lf.cast(.wasm).?;
2544 assert(reloc_parent == .none);
2545- try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset));
2546- code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
2547+ try wasm.addUavReloc(w.end, uav.val, uav.orig_ty, @intCast(offset));
2548+ try w.splatByteAll(0, ptr_width_bytes);
2549 return;
2550 },
2551 else => {},
2552 }
2553
2554- const uav_align = ip.indexToKey(uav.orig_ty).ptr_type.flags.alignment;
2555+ const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu);
2556 switch (try lf.lowerUav(pt, uav_val, uav_align, src_loc)) {
2557 .sym_index => {},
2558 .fail => |em| std.debug.panic("TODO rework lowerUav. internal error: {s}", .{em.msg}),
2559 }
2560
2561- const vaddr = try lf.getUavVAddr(uav_val, .{
2562+ const vaddr = lf.getUavVAddr(uav_val, .{
2563 .parent = reloc_parent,
2564- .offset = code.items.len,
2565+ .offset = w.end,
2566 .addend = @intCast(offset),
2567- });
2568+ }) catch |err| switch (err) {
2569+ error.OutOfMemory => return error.OutOfMemory,
2570+ else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}),
2571+ };
2572 const endian = target.cpu.arch.endian();
2573 switch (ptr_width_bytes) {
2574- 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
2575- 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
2576- 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
2577+ 2 => try w.writeInt(u16, @intCast(vaddr), endian),
2578+ 4 => try w.writeInt(u32, @intCast(vaddr), endian),
2579+ 8 => try w.writeInt(u64, vaddr, endian),
2580 else => unreachable,
2581 }
2582 }
2583@@ -835,10 +822,10 @@ fn lowerNavRef(
2584 lf: *link.File,
2585 pt: Zcu.PerThread,
2586 nav_index: InternPool.Nav.Index,
2587- code: *ArrayList(u8),
2588+ w: *std.Io.Writer,
2589 reloc_parent: link.File.RelocInfo.Parent,
2590 offset: u64,
2591-) GenerateSymbolError!void {
2592+) (GenerateSymbolError || std.Io.Writer.Error)!void {
2593 const zcu = pt.zcu;
2594 const gpa = zcu.gpa;
2595 const ip = &zcu.intern_pool;
2596@@ -848,10 +835,8 @@ fn lowerNavRef(
2597 const nav_ty = Type.fromInterned(ip.getNav(nav_index).typeOf(ip));
2598 const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
2599
2600- try code.ensureUnusedCapacity(gpa, ptr_width_bytes);
2601-
2602 if (!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) {
2603- code.appendNTimesAssumeCapacity(0xaa, ptr_width_bytes);
2604+ try w.splatByteAll(0xaa, ptr_width_bytes);
2605 return;
2606 }
2607
2608@@ -870,13 +855,13 @@ fn lowerNavRef(
2609 } else {
2610 try wasm.func_table_fixups.append(gpa, .{
2611 .table_index = @enumFromInt(gop.index),
2612- .offset = @intCast(code.items.len),
2613+ .offset = @intCast(w.end),
2614 });
2615 }
2616 } else {
2617 if (is_obj) {
2618 try wasm.out_relocs.append(gpa, .{
2619- .offset = @intCast(code.items.len),
2620+ .offset = @intCast(w.end),
2621 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(nav_index) },
2622 .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64,
2623 .addend = @intCast(offset),
2624@@ -885,12 +870,12 @@ fn lowerNavRef(
2625 try wasm.nav_fixups.ensureUnusedCapacity(gpa, 1);
2626 wasm.nav_fixups.appendAssumeCapacity(.{
2627 .navs_exe_index = try wasm.refNavExe(nav_index),
2628- .offset = @intCast(code.items.len),
2629+ .offset = @intCast(w.end),
2630 .addend = @intCast(offset),
2631 });
2632 }
2633 }
2634- code.appendNTimesAssumeCapacity(0, ptr_width_bytes);
2635+ try w.splatByteAll(0, ptr_width_bytes);
2636 return;
2637 },
2638 else => {},
2639@@ -898,14 +883,14 @@ fn lowerNavRef(
2640
2641 const vaddr = lf.getNavVAddr(pt, nav_index, .{
2642 .parent = reloc_parent,
2643- .offset = code.items.len,
2644+ .offset = w.end,
2645 .addend = @intCast(offset),
2646 }) catch @panic("TODO rework getNavVAddr");
2647 const endian = target.cpu.arch.endian();
2648 switch (ptr_width_bytes) {
2649- 2 => mem.writeInt(u16, code.addManyAsArrayAssumeCapacity(2), @intCast(vaddr), endian),
2650- 4 => mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @intCast(vaddr), endian),
2651- 8 => mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), vaddr, endian),
2652+ 2 => try w.writeInt(u16, @intCast(vaddr), endian),
2653+ 4 => try w.writeInt(u32, @intCast(vaddr), endian),
2654+ 8 => try w.writeInt(u64, vaddr, endian),
2655 else => unreachable,
2656 }
2657 }
2658@@ -962,6 +947,16 @@ pub fn genNavRef(
2659 },
2660 .link_once => unreachable,
2661 }
2662+ } else if (lf.cast(.elf2)) |elf| {
2663+ return .{ .sym_index = @intFromEnum(elf.navSymbol(zcu, nav_index) catch |err| switch (err) {
2664+ error.OutOfMemory => return error.OutOfMemory,
2665+ else => |e| return .{ .fail = try ErrorMsg.create(
2666+ zcu.gpa,
2667+ src_loc,
2668+ "linker failed to create a nav: {t}",
2669+ .{e},
2670+ ) },
2671+ }) };
2672 } else if (lf.cast(.macho)) |macho_file| {
2673 const zo = macho_file.getZigObject().?;
2674 switch (linkage) {
2675diff --git a/src/codegen/aarch64/Mir.zig b/src/codegen/aarch64/Mir.zig
2676index 318a51da95bfe1c047447f96cde8a83f993c9f80..be6478eae896f3d70d874e609d3c3d263946872a 100644
2677--- a/src/codegen/aarch64/Mir.zig
2678+++ b/src/codegen/aarch64/Mir.zig
2679@@ -56,13 +56,13 @@ pub fn emit(
2680 pt: Zcu.PerThread,
2681 src_loc: Zcu.LazySrcLoc,
2682 func_index: InternPool.Index,
2683- code: *std.ArrayListUnmanaged(u8),
2684+ atom_index: u32,
2685+ w: *std.Io.Writer,
2686 debug_output: link.File.DebugInfoOutput,
2687 ) !void {
2688 _ = debug_output;
2689 const zcu = pt.zcu;
2690 const ip = &zcu.intern_pool;
2691- const gpa = zcu.gpa;
2692 const func = zcu.funcInfo(func_index);
2693 const nav = ip.getNav(func.owner_nav);
2694 const mod = zcu.navFileScope(func.owner_nav).mod.?;
2695@@ -81,20 +81,19 @@ pub fn emit(
2696 @as(u5, @intCast(func_align.minStrict(.@"16").toByteUnits().?)),
2697 Instruction.size,
2698 ) - 1);
2699- try code.ensureUnusedCapacity(gpa, Instruction.size *
2700- (code_len + literals_align_gap + mir.literals.len));
2701- emitInstructionsForward(code, mir.prologue);
2702- emitInstructionsBackward(code, mir.body);
2703- const body_end: u32 = @intCast(code.items.len);
2704- emitInstructionsBackward(code, mir.epilogue);
2705- code.appendNTimesAssumeCapacity(0, Instruction.size * literals_align_gap);
2706- code.appendSliceAssumeCapacity(@ptrCast(mir.literals));
2707+ try w.rebase(w.end, Instruction.size * (code_len + literals_align_gap + mir.literals.len));
2708+ emitInstructionsForward(w, mir.prologue) catch unreachable;
2709+ emitInstructionsBackward(w, mir.body) catch unreachable;
2710+ const body_end: u32 = @intCast(w.end);
2711+ emitInstructionsBackward(w, mir.epilogue) catch unreachable;
2712+ w.splatByteAll(0, Instruction.size * literals_align_gap) catch unreachable;
2713+ w.writeAll(@ptrCast(mir.literals)) catch unreachable;
2714 mir_log.debug("", .{});
2715
2716 for (mir.nav_relocs) |nav_reloc| try emitReloc(
2717 lf,
2718 zcu,
2719- func.owner_nav,
2720+ atom_index,
2721 switch (try @import("../../codegen.zig").genNavRef(
2722 lf,
2723 pt,
2724@@ -112,7 +111,7 @@ pub fn emit(
2725 for (mir.uav_relocs) |uav_reloc| try emitReloc(
2726 lf,
2727 zcu,
2728- func.owner_nav,
2729+ atom_index,
2730 switch (try lf.lowerUav(
2731 pt,
2732 uav_reloc.uav.val,
2733@@ -129,7 +128,7 @@ pub fn emit(
2734 for (mir.lazy_relocs) |lazy_reloc| try emitReloc(
2735 lf,
2736 zcu,
2737- func.owner_nav,
2738+ atom_index,
2739 if (lf.cast(.elf)) |ef|
2740 ef.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(ef, pt, lazy_reloc.symbol) catch |err|
2741 return zcu.codegenFail(func.owner_nav, "{s} creating lazy symbol", .{@errorName(err)})
2742@@ -150,7 +149,7 @@ pub fn emit(
2743 for (mir.global_relocs) |global_reloc| try emitReloc(
2744 lf,
2745 zcu,
2746- func.owner_nav,
2747+ atom_index,
2748 if (lf.cast(.elf)) |ef|
2749 try ef.getGlobalSymbol(std.mem.span(global_reloc.name), null)
2750 else if (lf.cast(.macho)) |mf|
2751@@ -168,30 +167,30 @@ pub fn emit(
2752 var instruction = mir.body[literal_reloc.label];
2753 instruction.load_store.register_literal.group.imm19 += literal_reloc_offset;
2754 instruction.write(
2755- code.items[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size],
2756+ w.buffered()[body_end - Instruction.size * (1 + literal_reloc.label) ..][0..Instruction.size],
2757 );
2758 }
2759 }
2760
2761-fn emitInstructionsForward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void {
2762- for (instructions) |instruction| emitInstruction(code, instruction);
2763+fn emitInstructionsForward(w: *std.Io.Writer, instructions: []const Instruction) !void {
2764+ for (instructions) |instruction| try emitInstruction(w, instruction);
2765 }
2766-fn emitInstructionsBackward(code: *std.ArrayListUnmanaged(u8), instructions: []const Instruction) void {
2767+fn emitInstructionsBackward(w: *std.Io.Writer, instructions: []const Instruction) !void {
2768 var instruction_index = instructions.len;
2769 while (instruction_index > 0) {
2770 instruction_index -= 1;
2771- emitInstruction(code, instructions[instruction_index]);
2772+ try emitInstruction(w, instructions[instruction_index]);
2773 }
2774 }
2775-fn emitInstruction(code: *std.ArrayListUnmanaged(u8), instruction: Instruction) void {
2776+fn emitInstruction(w: *std.Io.Writer, instruction: Instruction) !void {
2777 mir_log.debug(" {f}", .{instruction});
2778- instruction.write(code.addManyAsArrayAssumeCapacity(Instruction.size));
2779+ instruction.write(try w.writableArray(Instruction.size));
2780 }
2781
2782 fn emitReloc(
2783 lf: *link.File,
2784 zcu: *Zcu,
2785- owner_nav: InternPool.Nav.Index,
2786+ atom_index: u32,
2787 sym_index: u32,
2788 instruction: Instruction,
2789 offset: u32,
2790@@ -202,7 +201,7 @@ fn emitReloc(
2791 else => unreachable,
2792 .data_processing_immediate => |decoded| if (lf.cast(.elf)) |ef| {
2793 const zo = ef.zigObjectPtr().?;
2794- const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
2795+ const atom = zo.symbol(atom_index).atom(ef).?;
2796 const r_type: std.elf.R_AARCH64 = switch (decoded.decode()) {
2797 else => unreachable,
2798 .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
2799@@ -221,7 +220,7 @@ fn emitReloc(
2800 }, zo);
2801 } else if (lf.cast(.macho)) |mf| {
2802 const zo = mf.getZigObject().?;
2803- const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
2804+ const atom = zo.symbols.items[atom_index].getAtom(mf).?;
2805 switch (decoded.decode()) {
2806 else => unreachable,
2807 .pc_relative_addressing => |pc_relative_addressing| switch (pc_relative_addressing.group.op) {
2808@@ -260,7 +259,7 @@ fn emitReloc(
2809 },
2810 .branch_exception_generating_system => |decoded| if (lf.cast(.elf)) |ef| {
2811 const zo = ef.zigObjectPtr().?;
2812- const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
2813+ const atom = zo.symbol(atom_index).atom(ef).?;
2814 const r_type: std.elf.R_AARCH64 = switch (decoded.decode().unconditional_branch_immediate.group.op) {
2815 .b => .JUMP26,
2816 .bl => .CALL26,
2817@@ -272,7 +271,7 @@ fn emitReloc(
2818 }, zo);
2819 } else if (lf.cast(.macho)) |mf| {
2820 const zo = mf.getZigObject().?;
2821- const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
2822+ const atom = zo.symbols.items[atom_index].getAtom(mf).?;
2823 try atom.addReloc(mf, .{
2824 .tag = .@"extern",
2825 .offset = offset,
2826@@ -289,7 +288,7 @@ fn emitReloc(
2827 },
2828 .load_store => |decoded| if (lf.cast(.elf)) |ef| {
2829 const zo = ef.zigObjectPtr().?;
2830- const atom = zo.symbol(try zo.getOrCreateMetadataForNav(zcu, owner_nav)).atom(ef).?;
2831+ const atom = zo.symbol(atom_index).atom(ef).?;
2832 const r_type: std.elf.R_AARCH64 = switch (decoded.decode().register_unsigned_immediate.decode()) {
2833 .integer => |integer| switch (integer.decode()) {
2834 .unallocated, .prfm => unreachable,
2835@@ -316,7 +315,7 @@ fn emitReloc(
2836 }, zo);
2837 } else if (lf.cast(.macho)) |mf| {
2838 const zo = mf.getZigObject().?;
2839- const atom = zo.symbols.items[try zo.getOrCreateMetadataForNav(mf, owner_nav)].getAtom(mf).?;
2840+ const atom = zo.symbols.items[atom_index].getAtom(mf).?;
2841 try atom.addReloc(mf, .{
2842 .tag = .@"extern",
2843 .offset = offset,
2844diff --git a/src/dev.zig b/src/dev.zig
2845index ac30cedecd8318da8f38e96e45d763fc2d70bc1f..266796a2dc2dda022bd7c4c3027c950a446d9dc9 100644
2846--- a/src/dev.zig
2847+++ b/src/dev.zig
2848@@ -97,6 +97,7 @@ pub const Env = enum {
2849 .lld_linker,
2850 .coff_linker,
2851 .elf_linker,
2852+ .elf2_linker,
2853 .macho_linker,
2854 .c_linker,
2855 .wasm_linker,
2856@@ -163,6 +164,7 @@ pub const Env = enum {
2857 .incremental,
2858 .aarch64_backend,
2859 .elf_linker,
2860+ .elf2_linker,
2861 => true,
2862 else => Env.sema.supports(feature),
2863 },
2864@@ -210,6 +212,7 @@ pub const Env = enum {
2865 .legalize,
2866 .x86_64_backend,
2867 .elf_linker,
2868+ .elf2_linker,
2869 => true,
2870 else => Env.sema.supports(feature),
2871 },
2872@@ -282,6 +285,7 @@ pub const Feature = enum {
2873 lld_linker,
2874 coff_linker,
2875 elf_linker,
2876+ elf2_linker,
2877 macho_linker,
2878 c_linker,
2879 wasm_linker,
2880diff --git a/src/link.zig b/src/link.zig
2881index 27cd6620e359284f0a6398aa23a5f8ade66446d7..277013efb3094cee331db13b15fd1e5d2d08d7e4 100644
2882--- a/src/link.zig
2883+++ b/src/link.zig
2884@@ -219,6 +219,7 @@ pub const Diags = struct {
2885 }
2886
2887 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
2888+ @branchHint(.cold);
2889 return addErrorSourceLocation(diags, .none, format, args);
2890 }
2891
2892@@ -529,7 +530,7 @@ pub const File = struct {
2893 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
2894 return &lld.base;
2895 }
2896- switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
2897+ switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
2898 .plan9 => return error.UnsupportedObjectFormat,
2899 inline else => |tag| {
2900 dev.check(tag.devFeature());
2901@@ -552,7 +553,7 @@ pub const File = struct {
2902 const lld: *Lld = try .createEmpty(arena, comp, emit, options);
2903 return &lld.base;
2904 }
2905- switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
2906+ switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
2907 .plan9 => return error.UnsupportedObjectFormat,
2908 inline else => |tag| {
2909 dev.check(tag.devFeature());
2910@@ -579,7 +580,8 @@ pub const File = struct {
2911 const emit = base.emit;
2912 if (base.child_pid) |pid| {
2913 if (builtin.os.tag == .windows) {
2914- base.cast(.coff).?.ptraceAttach(pid) catch |err| {
2915+ const coff_file = base.cast(.coff).?;
2916+ coff_file.ptraceAttach(pid) catch |err| {
2917 log.warn("attaching failed with error: {s}", .{@errorName(err)});
2918 };
2919 } else {
2920@@ -597,8 +599,11 @@ pub const File = struct {
2921 .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
2922 log.warn("ptrace failure: {s}", .{@errorName(err)});
2923 },
2924- .macos => base.cast(.macho).?.ptraceAttach(pid) catch |err| {
2925- log.warn("attaching failed with error: {s}", .{@errorName(err)});
2926+ .macos => {
2927+ const macho_file = base.cast(.macho).?;
2928+ macho_file.ptraceAttach(pid) catch |err| {
2929+ log.warn("attaching failed with error: {s}", .{@errorName(err)});
2930+ };
2931 },
2932 .windows => unreachable,
2933 else => return error.HotSwapUnavailableOnHostOperatingSystem,
2934@@ -613,6 +618,20 @@ pub const File = struct {
2935 .mode = determineMode(output_mode, link_mode),
2936 });
2937 },
2938+ .elf2 => {
2939+ const elf = base.cast(.elf2).?;
2940+ if (base.file == null) {
2941+ elf.mf.file = try base.emit.root_dir.handle.createFile(base.emit.sub_path, .{
2942+ .truncate = false,
2943+ .read = true,
2944+ .mode = determineMode(comp.config.output_mode, comp.config.link_mode),
2945+ });
2946+ base.file = elf.mf.file;
2947+ try elf.mf.ensureTotalCapacity(
2948+ @intCast(elf.mf.nodes.items[0].location().resolve(&elf.mf)[1]),
2949+ );
2950+ }
2951+ },
2952 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
2953 .plan9 => unreachable,
2954 }
2955@@ -669,14 +688,30 @@ pub const File = struct {
2956
2957 if (base.child_pid) |pid| {
2958 switch (builtin.os.tag) {
2959- .macos => base.cast(.macho).?.ptraceDetach(pid) catch |err| {
2960- log.warn("detaching failed with error: {s}", .{@errorName(err)});
2961+ .macos => {
2962+ const macho_file = base.cast(.macho).?;
2963+ macho_file.ptraceDetach(pid) catch |err| {
2964+ log.warn("detaching failed with error: {s}", .{@errorName(err)});
2965+ };
2966+ },
2967+ .windows => {
2968+ const coff_file = base.cast(.coff).?;
2969+ coff_file.ptraceDetach(pid);
2970 },
2971- .windows => base.cast(.coff).?.ptraceDetach(pid),
2972 else => return error.HotSwapUnavailableOnHostOperatingSystem,
2973 }
2974 }
2975 },
2976+ .elf2 => {
2977+ const elf = base.cast(.elf2).?;
2978+ if (base.file) |f| {
2979+ elf.mf.unmap();
2980+ assert(elf.mf.file.handle == f.handle);
2981+ elf.mf.file = undefined;
2982+ f.close();
2983+ base.file = null;
2984+ }
2985+ },
2986 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
2987 .plan9 => unreachable,
2988 }
2989@@ -793,6 +828,7 @@ pub const File = struct {
2990 .spirv => {},
2991 .goff, .xcoff => {},
2992 .plan9 => unreachable,
2993+ .elf2 => {},
2994 inline else => |tag| {
2995 dev.check(tag.devFeature());
2996 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
2997@@ -825,6 +861,26 @@ pub const File = struct {
2998 }
2999 }
3000
3001+ pub fn idle(base: *File, tid: Zcu.PerThread.Id) !bool {
3002+ switch (base.tag) {
3003+ else => return false,
3004+ inline .elf2 => |tag| {
3005+ dev.check(tag.devFeature());
3006+ return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);
3007+ },
3008+ }
3009+ }
3010+
3011+ pub fn updateErrorData(base: *File, pt: Zcu.PerThread) !void {
3012+ switch (base.tag) {
3013+ else => {},
3014+ inline .elf2 => |tag| {
3015+ dev.check(tag.devFeature());
3016+ return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);
3017+ },
3018+ }
3019+ }
3020+
3021 pub const FlushError = error{
3022 /// Indicates an error will be present in `Compilation.link_diags`.
3023 LinkFailure,
3024@@ -1099,7 +1155,7 @@ pub const File = struct {
3025 if (base.zcu_object_basename != null) return;
3026
3027 switch (base.tag) {
3028- inline .wasm => |tag| {
3029+ inline .elf2, .wasm => |tag| {
3030 dev.check(tag.devFeature());
3031 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
3032 },
3033@@ -1110,6 +1166,7 @@ pub const File = struct {
3034 pub const Tag = enum {
3035 coff,
3036 elf,
3037+ elf2,
3038 macho,
3039 c,
3040 wasm,
3041@@ -1123,6 +1180,7 @@ pub const File = struct {
3042 return switch (tag) {
3043 .coff => Coff,
3044 .elf => Elf,
3045+ .elf2 => Elf2,
3046 .macho => MachO,
3047 .c => C,
3048 .wasm => Wasm,
3049@@ -1134,10 +1192,10 @@ pub const File = struct {
3050 };
3051 }
3052
3053- fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag {
3054+ fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {
3055 return switch (ofmt) {
3056 .coff => .coff,
3057- .elf => .elf,
3058+ .elf => if (use_new_linker) .elf2 else .elf,
3059 .macho => .macho,
3060 .wasm => .wasm,
3061 .plan9 => .plan9,
3062@@ -1223,6 +1281,7 @@ pub const File = struct {
3063 pub const C = @import("link/C.zig");
3064 pub const Coff = @import("link/Coff.zig");
3065 pub const Elf = @import("link/Elf.zig");
3066+ pub const Elf2 = @import("link/Elf2.zig");
3067 pub const MachO = @import("link/MachO.zig");
3068 pub const SpirV = @import("link/SpirV.zig");
3069 pub const Wasm = @import("link/Wasm.zig");
3070@@ -1548,6 +1607,9 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
3071 }
3072 }
3073 }
3074+pub fn doIdleTask(comp: *Compilation, tid: usize) error{ OutOfMemory, LinkFailure }!bool {
3075+ return if (comp.bin_file) |lf| lf.idle(@enumFromInt(tid)) else false;
3076+}
3077 /// After the main pipeline is done, but before flush, the compilation may need to link one final
3078 /// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
3079 /// by then, we expose this function which can be called directly.
3080@@ -1573,6 +1635,13 @@ pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
3081 };
3082 }
3083 }
3084+pub fn updateErrorData(pt: Zcu.PerThread) void {
3085+ const comp = pt.zcu.comp;
3086+ if (comp.bin_file) |lf| lf.updateErrorData(pt) catch |err| switch (err) {
3087+ error.OutOfMemory => comp.link_diags.setAllocFailure(),
3088+ error.LinkFailure => {},
3089+ };
3090+}
3091
3092 /// Provided by the CLI, processed into `LinkInput` instances at the start of
3093 /// the compilation pipeline.
3094diff --git a/src/link/Coff.zig b/src/link/Coff.zig
3095index e016473531aede3998c41f42a85745fbf017c63c..f3e3b3d0b530d498f1b8d4518c1d9c06d4a69baa 100644
3096--- a/src/link/Coff.zig
3097+++ b/src/link/Coff.zig
3098@@ -953,7 +953,7 @@ fn writeOffsetTableEntry(coff: *Coff, index: usize) !void {
3099 }
3100
3101 fn markRelocsDirtyByTarget(coff: *Coff, target: SymbolWithLoc) void {
3102- if (!coff.base.comp.incremental) return;
3103+ if (!coff.base.comp.config.incremental) return;
3104 // TODO: reverse-lookup might come in handy here
3105 for (coff.relocs.values()) |*relocs| {
3106 for (relocs.items) |*reloc| {
3107@@ -964,7 +964,7 @@ fn markRelocsDirtyByTarget(coff: *Coff, target: SymbolWithLoc) void {
3108 }
3109
3110 fn markRelocsDirtyByAddress(coff: *Coff, addr: u32) void {
3111- if (!coff.base.comp.incremental) return;
3112+ if (!coff.base.comp.config.incremental) return;
3113 const got_moved = blk: {
3114 const sect_id = coff.got_section_index orelse break :blk false;
3115 break :blk coff.sections.items(.header)[sect_id].virtual_address >= addr;
3116@@ -1111,20 +1111,24 @@ pub fn updateFunc(
3117
3118 coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
3119
3120- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3121- defer code_buffer.deinit(gpa);
3122+ var aw: std.Io.Writer.Allocating = .init(gpa);
3123+ defer aw.deinit();
3124
3125- try codegen.emitFunction(
3126+ codegen.emitFunction(
3127 &coff.base,
3128 pt,
3129 zcu.navSrcLoc(nav_index),
3130 func_index,
3131+ coff.getAtom(atom_index).getSymbolIndex().?,
3132 mir,
3133- &code_buffer,
3134+ &aw.writer,
3135 .none,
3136- );
3137+ ) catch |err| switch (err) {
3138+ error.WriteFailed => return error.OutOfMemory,
3139+ else => |e| return e,
3140+ };
3141
3142- try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION);
3143+ try coff.updateNavCode(pt, nav_index, aw.written(), .FUNCTION);
3144
3145 // Exports will be updated by `Zcu.processExports` after the update.
3146 }
3147@@ -1145,18 +1149,18 @@ fn lowerConst(
3148 ) !LowerConstResult {
3149 const gpa = coff.base.comp.gpa;
3150
3151- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3152- defer code_buffer.deinit(gpa);
3153+ var aw: std.Io.Writer.Allocating = .init(gpa);
3154+ defer aw.deinit();
3155
3156 const atom_index = try coff.createAtom();
3157 const sym = coff.getAtom(atom_index).getSymbolPtr(coff);
3158 try coff.setSymbolName(sym, name);
3159 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(sect_id + 1));
3160
3161- try codegen.generateSymbol(&coff.base, pt, src_loc, val, &code_buffer, .{
3162+ try codegen.generateSymbol(&coff.base, pt, src_loc, val, &aw.writer, .{
3163 .atom_index = coff.getAtom(atom_index).getSymbolIndex().?,
3164 });
3165- const code = code_buffer.items;
3166+ const code = aw.written();
3167
3168 const atom = coff.getAtomPtr(atom_index);
3169 atom.size = @intCast(code.len);
3170@@ -1170,7 +1174,7 @@ fn lowerConst(
3171 log.debug("allocated atom for {s} at 0x{x}", .{ name, atom.getSymbol(coff).value });
3172 log.debug(" (required alignment 0x{x})", .{required_alignment});
3173
3174- try coff.writeAtom(atom_index, code, coff.base.comp.incremental);
3175+ try coff.writeAtom(atom_index, code, coff.base.comp.config.incremental);
3176
3177 return .{ .ok = atom_index };
3178 }
3179@@ -1214,19 +1218,22 @@ pub fn updateNav(
3180
3181 coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
3182
3183- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3184- defer code_buffer.deinit(gpa);
3185+ var aw: std.Io.Writer.Allocating = .init(gpa);
3186+ defer aw.deinit();
3187
3188- try codegen.generateSymbol(
3189+ codegen.generateSymbol(
3190 &coff.base,
3191 pt,
3192 zcu.navSrcLoc(nav_index),
3193 nav_init,
3194- &code_buffer,
3195+ &aw.writer,
3196 .{ .atom_index = atom.getSymbolIndex().? },
3197- );
3198+ ) catch |err| switch (err) {
3199+ error.WriteFailed => return error.OutOfMemory,
3200+ else => |e| return e,
3201+ };
3202
3203- try coff.updateNavCode(pt, nav_index, code_buffer.items, .NULL);
3204+ try coff.updateNavCode(pt, nav_index, aw.written(), .NULL);
3205 }
3206
3207 // Exports will be updated by `Zcu.processExports` after the update.
3208@@ -1244,8 +1251,8 @@ fn updateLazySymbolAtom(
3209 const gpa = comp.gpa;
3210
3211 var required_alignment: InternPool.Alignment = .none;
3212- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3213- defer code_buffer.deinit(gpa);
3214+ var aw: std.Io.Writer.Allocating = .init(gpa);
3215+ defer aw.deinit();
3216
3217 const name = try allocPrint(gpa, "__lazy_{s}_{f}", .{
3218 @tagName(sym.kind),
3219@@ -1262,11 +1269,11 @@ fn updateLazySymbolAtom(
3220 src,
3221 sym,
3222 &required_alignment,
3223- &code_buffer,
3224+ &aw.writer,
3225 .none,
3226 .{ .atom_index = local_sym_index },
3227 );
3228- const code = code_buffer.items;
3229+ const code = aw.written();
3230
3231 const atom = coff.getAtomPtr(atom_index);
3232 const symbol = atom.getSymbolPtr(coff);
3233@@ -1285,7 +1292,7 @@ fn updateLazySymbolAtom(
3234 symbol.value = vaddr;
3235
3236 try coff.addGotEntry(.{ .sym_index = local_sym_index });
3237- try coff.writeAtom(atom_index, code, coff.base.comp.incremental);
3238+ try coff.writeAtom(atom_index, code, coff.base.comp.config.incremental);
3239 }
3240
3241 pub fn getOrCreateAtomForLazySymbol(
3242@@ -1437,7 +1444,7 @@ fn updateNavCode(
3243 };
3244 }
3245
3246- coff.writeAtom(atom_index, code, coff.base.comp.incremental) catch |err| switch (err) {
3247+ coff.writeAtom(atom_index, code, coff.base.comp.config.incremental) catch |err| switch (err) {
3248 error.OutOfMemory => return error.OutOfMemory,
3249 else => |e| return coff.base.cgFail(nav_index, "failed to write atom: {s}", .{@errorName(e)}),
3250 };
3251@@ -1539,14 +1546,12 @@ pub fn updateExports(
3252 sym.section_number = @as(coff_util.SectionNumber, @enumFromInt(metadata.section + 1));
3253 sym.type = atom.getSymbol(coff).type;
3254
3255- switch (exp.opts.linkage) {
3256- .strong => {
3257- sym.storage_class = .EXTERNAL;
3258- },
3259- .internal => @panic("TODO Internal"),
3260+ sym.storage_class = switch (exp.opts.linkage) {
3261+ .internal => .EXTERNAL,
3262+ .strong => .EXTERNAL,
3263 .weak => @panic("TODO WeakExternal"),
3264 else => unreachable,
3265- }
3266+ };
3267
3268 try coff.resolveGlobalSymbol(sym_loc);
3269 }
3270diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
3271index f4af5f4148ad11f2eeb0688ccd11a39120404d6b..595e3583d5d540bdd0fe0ce1727d0a25eed72f50 100644
3272--- a/src/link/Dwarf.zig
3273+++ b/src/link/Dwarf.zig
3274@@ -2126,19 +2126,22 @@ pub const WipNav = struct {
3275 const size = if (ty.hasRuntimeBits(wip_nav.pt.zcu)) ty.abiSize(wip_nav.pt.zcu) else 0;
3276 try diw.writeUleb128(size);
3277 if (size == 0) return;
3278- var bytes = wip_nav.debug_info.toArrayList();
3279- defer wip_nav.debug_info = .fromArrayList(wip_nav.dwarf.gpa, &bytes);
3280- const old_len = bytes.items.len;
3281+ const old_end = wip_nav.debug_info.writer.end;
3282 try codegen.generateSymbol(
3283 wip_nav.dwarf.bin_file,
3284 wip_nav.pt,
3285 src_loc,
3286 val,
3287- &bytes,
3288+ &wip_nav.debug_info.writer,
3289 .{ .debug_output = .{ .dwarf = wip_nav } },
3290 );
3291- if (old_len + size != bytes.items.len) {
3292- std.debug.print("{f} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), size, bytes.items.len - old_len });
3293+ if (old_end + size != wip_nav.debug_info.writer.end) {
3294+ std.debug.print("{f} [{}]: {} != {}\n", .{
3295+ ty.fmt(wip_nav.pt),
3296+ ty.toIntern(),
3297+ size,
3298+ wip_nav.debug_info.writer.end - old_end,
3299+ });
3300 unreachable;
3301 }
3302 }
3303@@ -6429,7 +6432,7 @@ fn sleb128Bytes(value: anytype) u32 {
3304 /// overrides `-fno-incremental` for testing incremental debug info until `-fincremental` is functional
3305 const force_incremental = false;
3306 inline fn incremental(dwarf: Dwarf) bool {
3307- return force_incremental or dwarf.bin_file.comp.incremental;
3308+ return force_incremental or dwarf.bin_file.comp.config.incremental;
3309 }
3310
3311 const Allocator = std.mem.Allocator;
3312diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig
3313index b74406c368d5cc4eb3da41365236bc79387bf7c2..d7e3cf62f3a378970682797fb538227d9c70795d 100644
3314--- a/src/link/Elf/LinkerDefined.zig
3315+++ b/src/link/Elf/LinkerDefined.zig
3316@@ -47,7 +47,7 @@ fn newSymbolAssumeCapacity(self: *LinkerDefined, name_off: u32, elf_file: *Elf)
3317 const esym = self.symtab.addOneAssumeCapacity();
3318 esym.* = .{
3319 .st_name = name_off,
3320- .st_info = elf.STB_WEAK << 4,
3321+ .st_info = @as(u8, elf.STB_WEAK) << 4,
3322 .st_other = @intFromEnum(elf.STV.HIDDEN),
3323 .st_shndx = elf.SHN_ABS,
3324 .st_value = 0,
3325diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig
3326index 8c79def16bff14a9878ab22cea2ee6e1aca7c160..4dce40e37094e9a48db41d35ce189af38b63c287 100644
3327--- a/src/link/Elf/SharedObject.zig
3328+++ b/src/link/Elf/SharedObject.zig
3329@@ -105,7 +105,7 @@ pub fn parseHeader(
3330 if (amt != buf.len) return error.UnexpectedEndOfFile;
3331 }
3332 if (!mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")) return error.BadMagic;
3333- if (ehdr.e_ident[elf.EI_VERSION] != 1) return error.BadElfVersion;
3334+ if (ehdr.e_ident[elf.EI.VERSION] != 1) return error.BadElfVersion;
3335 if (ehdr.e_type != elf.ET.DYN) return error.NotSharedObject;
3336
3337 if (target.toElfMachine() != ehdr.e_machine)
3338diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig
3339index bf65d04e4a13b3980749bfcc1af24f52d4ac3b49..a3ede11dc4003cd0f9d1f671dfccc5c095b16a82 100644
3340--- a/src/link/Elf/ZigObject.zig
3341+++ b/src/link/Elf/ZigObject.zig
3342@@ -277,8 +277,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
3343 pt,
3344 .{ .kind = .code, .ty = .anyerror_type },
3345 metadata.text_symbol_index,
3346- ) catch |err| return switch (err) {
3347- error.CodegenFail => error.LinkFailure,
3348+ ) catch |err| switch (err) {
3349+ error.CodegenFail => return error.LinkFailure,
3350 else => |e| return e,
3351 };
3352 if (metadata.rodata_state != .unused) self.updateLazySymbol(
3353@@ -286,8 +286,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
3354 pt,
3355 .{ .kind = .const_data, .ty = .anyerror_type },
3356 metadata.rodata_symbol_index,
3357- ) catch |err| return switch (err) {
3358- error.CodegenFail => error.LinkFailure,
3359+ ) catch |err| switch (err) {
3360+ error.CodegenFail => return error.LinkFailure,
3361 else => |e| return e,
3362 };
3363 }
3364@@ -1533,22 +1533,26 @@ pub fn updateFunc(
3365 const sym_index = try self.getOrCreateMetadataForNav(zcu, func.owner_nav);
3366 self.atom(self.symbol(sym_index).ref.index).?.freeRelocs(self);
3367
3368- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3369- defer code_buffer.deinit(gpa);
3370+ var aw: std.Io.Writer.Allocating = .init(gpa);
3371+ defer aw.deinit();
3372
3373 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
3374 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
3375
3376- try codegen.emitFunction(
3377+ codegen.emitFunction(
3378 &elf_file.base,
3379 pt,
3380 zcu.navSrcLoc(func.owner_nav),
3381 func_index,
3382+ sym_index,
3383 mir,
3384- &code_buffer,
3385+ &aw.writer,
3386 if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
3387- );
3388- const code = code_buffer.items;
3389+ ) catch |err| switch (err) {
3390+ error.WriteFailed => return error.OutOfMemory,
3391+ else => |e| return e,
3392+ };
3393+ const code = aw.written();
3394
3395 const shndx = try self.getNavShdrIndex(elf_file, zcu, func.owner_nav, sym_index, code);
3396 log.debug("setting shdr({x},{s}) for {f}", .{
3397@@ -1663,21 +1667,24 @@ pub fn updateNav(
3398 const sym_index = try self.getOrCreateMetadataForNav(zcu, nav_index);
3399 self.symbol(sym_index).atom(elf_file).?.freeRelocs(self);
3400
3401- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3402- defer code_buffer.deinit(zcu.gpa);
3403+ var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
3404+ defer aw.deinit();
3405
3406 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
3407 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
3408
3409- try codegen.generateSymbol(
3410+ codegen.generateSymbol(
3411 &elf_file.base,
3412 pt,
3413 zcu.navSrcLoc(nav_index),
3414 Value.fromInterned(nav_init),
3415- &code_buffer,
3416+ &aw.writer,
3417 .{ .atom_index = sym_index },
3418- );
3419- const code = code_buffer.items;
3420+ ) catch |err| switch (err) {
3421+ error.WriteFailed => return error.OutOfMemory,
3422+ else => |e| return e,
3423+ };
3424+ const code = aw.written();
3425
3426 const shndx = try self.getNavShdrIndex(elf_file, zcu, nav_index, sym_index, code);
3427 log.debug("setting shdr({x},{s}) for {f}", .{
3428@@ -1722,8 +1729,8 @@ fn updateLazySymbol(
3429 const gpa = zcu.gpa;
3430
3431 var required_alignment: InternPool.Alignment = .none;
3432- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3433- defer code_buffer.deinit(gpa);
3434+ var aw: std.Io.Writer.Allocating = .init(gpa);
3435+ defer aw.deinit();
3436
3437 const name_str_index = blk: {
3438 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
3439@@ -1734,18 +1741,20 @@ fn updateLazySymbol(
3440 break :blk try self.strtab.insert(gpa, name);
3441 };
3442
3443- const src = Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse Zcu.LazySrcLoc.unneeded;
3444- try codegen.generateLazySymbol(
3445+ codegen.generateLazySymbol(
3446 &elf_file.base,
3447 pt,
3448- src,
3449+ Type.fromInterned(sym.ty).srcLocOrNull(zcu) orelse .unneeded,
3450 sym,
3451 &required_alignment,
3452- &code_buffer,
3453+ &aw.writer,
3454 .none,
3455 .{ .atom_index = symbol_index },
3456- );
3457- const code = code_buffer.items;
3458+ ) catch |err| switch (err) {
3459+ error.WriteFailed => return error.OutOfMemory,
3460+ else => |e| return e,
3461+ };
3462+ const code = aw.written();
3463
3464 const output_section_index = switch (sym.kind) {
3465 .code => if (self.text_index) |sym_index|
3466@@ -1807,21 +1816,24 @@ fn lowerConst(
3467 ) !codegen.SymbolResult {
3468 const gpa = pt.zcu.gpa;
3469
3470- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
3471- defer code_buffer.deinit(gpa);
3472+ var aw: std.Io.Writer.Allocating = .init(gpa);
3473+ defer aw.deinit();
3474
3475 const name_off = try self.addString(gpa, name);
3476 const sym_index = try self.newSymbolWithAtom(gpa, name_off);
3477
3478- try codegen.generateSymbol(
3479+ codegen.generateSymbol(
3480 &elf_file.base,
3481 pt,
3482 src_loc,
3483 val,
3484- &code_buffer,
3485+ &aw.writer,
3486 .{ .atom_index = sym_index },
3487- );
3488- const code = code_buffer.items;
3489+ ) catch |err| switch (err) {
3490+ error.WriteFailed => return error.OutOfMemory,
3491+ else => |e| return e,
3492+ };
3493+ const code = aw.written();
3494
3495 const local_sym = self.symbol(sym_index);
3496 const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
3497diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig
3498new file mode 100644
3499index 0000000000000000000000000000000000000000..e43ebf2639ad88db8d125d2a79877173d771f73d
3500--- /dev/null
3501+++ b/src/link/Elf2.zig
3502@@ -0,0 +1,2036 @@
3503+base: link.File,
3504+mf: MappedFile,
3505+nodes: std.MultiArrayList(Node),
3506+symtab: std.ArrayList(Symbol),
3507+shstrtab: StringTable,
3508+strtab: StringTable,
3509+globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),
3510+navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Symbol.Index),
3511+uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
3512+lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
3513+ map: std.AutoArrayHashMapUnmanaged(InternPool.Index, Symbol.Index),
3514+ pending_index: u32,
3515+}),
3516+pending_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
3517+ alignment: InternPool.Alignment,
3518+ src_loc: Zcu.LazySrcLoc,
3519+}),
3520+relocs: std.ArrayList(Reloc),
3521+/// This is hiding actual bugs with global symbols! Reconsider once they are implemented correctly.
3522+entry_hack: Symbol.Index,
3523+
3524+pub const Node = union(enum) {
3525+ file,
3526+ ehdr,
3527+ shdr,
3528+ segment: u32,
3529+ section: Symbol.Index,
3530+ nav: InternPool.Nav.Index,
3531+ uav: InternPool.Index,
3532+ lazy_code: InternPool.Index,
3533+ lazy_const_data: InternPool.Index,
3534+
3535+ pub const Tag = @typeInfo(Node).@"union".tag_type.?;
3536+
3537+ const known_count = @typeInfo(@TypeOf(known)).@"struct".fields.len;
3538+ const known = known: {
3539+ const Known = enum {
3540+ file,
3541+ seg_rodata,
3542+ ehdr,
3543+ phdr,
3544+ shdr,
3545+ seg_text,
3546+ seg_data,
3547+ };
3548+ var mut_known: std.enums.EnumFieldStruct(
3549+ Known,
3550+ MappedFile.Node.Index,
3551+ null,
3552+ ) = undefined;
3553+ for (@typeInfo(Known).@"enum".fields) |field|
3554+ @field(mut_known, field.name) = @enumFromInt(field.value);
3555+ break :known mut_known;
3556+ };
3557+
3558+ comptime {
3559+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
3560+ }
3561+};
3562+
3563+pub const StringTable = struct {
3564+ map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
3565+ size: u32,
3566+
3567+ const Context = struct {
3568+ slice: []const u8,
3569+
3570+ pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool {
3571+ return lhs_key == rhs_key;
3572+ }
3573+
3574+ pub fn hash(ctx: Context, key: u32) u64 {
3575+ return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0));
3576+ }
3577+ };
3578+
3579+ const Adapter = struct {
3580+ slice: []const u8,
3581+
3582+ pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32) bool {
3583+ return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
3584+ adapter.slice[rhs_key + lhs_key.len] == 0;
3585+ }
3586+
3587+ pub fn hash(_: Adapter, key: []const u8) u64 {
3588+ assert(std.mem.indexOfScalar(u8, key, 0) == null);
3589+ return std.hash_map.hashString(key);
3590+ }
3591+ };
3592+
3593+ pub fn get(
3594+ st: *StringTable,
3595+ gpa: std.mem.Allocator,
3596+ mf: *MappedFile,
3597+ ni: MappedFile.Node.Index,
3598+ key: []const u8,
3599+ ) !u32 {
3600+ const slice_const = ni.sliceConst(mf);
3601+ const gop = try st.map.getOrPutContextAdapted(
3602+ gpa,
3603+ key,
3604+ StringTable.Adapter{ .slice = slice_const },
3605+ .{ .slice = slice_const },
3606+ );
3607+ if (gop.found_existing) return gop.key_ptr.*;
3608+ const old_size = st.size;
3609+ const new_size: u32 = @intCast(old_size + key.len + 1);
3610+ st.size = new_size;
3611+ try ni.resize(mf, gpa, new_size);
3612+ const slice = ni.slice(mf)[old_size..];
3613+ @memcpy(slice[0..key.len], key);
3614+ slice[key.len] = 0;
3615+ gop.key_ptr.* = old_size;
3616+ return old_size;
3617+ }
3618+};
3619+
3620+pub const Symbol = struct {
3621+ ni: MappedFile.Node.Index,
3622+ /// Relocations contained within this symbol
3623+ loc_relocs: Reloc.Index,
3624+ /// Relocations targeting this symbol
3625+ target_relocs: Reloc.Index,
3626+ unused: u32 = 0,
3627+
3628+ pub const Index = enum(u32) {
3629+ null,
3630+ symtab,
3631+ shstrtab,
3632+ strtab,
3633+ rodata,
3634+ text,
3635+ data,
3636+ tdata,
3637+ _,
3638+
3639+ pub fn get(si: Symbol.Index, elf: *Elf) *Symbol {
3640+ return &elf.symtab.items[@intFromEnum(si)];
3641+ }
3642+
3643+ pub fn node(si: Symbol.Index, elf: *Elf) MappedFile.Node.Index {
3644+ const ni = si.get(elf).ni;
3645+ assert(ni != .none);
3646+ return ni;
3647+ }
3648+
3649+ pub const InitOptions = struct {
3650+ name: []const u8 = "",
3651+ size: std.elf.Word = 0,
3652+ type: std.elf.STT,
3653+ bind: std.elf.STB = .LOCAL,
3654+ visibility: std.elf.STV = .DEFAULT,
3655+ shndx: std.elf.Section = std.elf.SHN_UNDEF,
3656+ };
3657+ pub fn init(si: Symbol.Index, elf: *Elf, opts: InitOptions) !void {
3658+ const name_entry = try elf.string(.strtab, opts.name);
3659+ try Symbol.Index.symtab.node(elf).resize(
3660+ &elf.mf,
3661+ elf.base.comp.gpa,
3662+ @as(usize, switch (elf.identClass()) {
3663+ .NONE, _ => unreachable,
3664+ .@"32" => @sizeOf(std.elf.Elf32.Sym),
3665+ .@"64" => @sizeOf(std.elf.Elf64.Sym),
3666+ }) * elf.symtab.items.len,
3667+ );
3668+ switch (elf.symPtr(si)) {
3669+ inline else => |sym| sym.* = .{
3670+ .name = name_entry,
3671+ .value = 0,
3672+ .size = opts.size,
3673+ .info = .{
3674+ .type = opts.type,
3675+ .bind = opts.bind,
3676+ },
3677+ .other = .{
3678+ .visibility = opts.visibility,
3679+ },
3680+ .shndx = opts.shndx,
3681+ },
3682+ }
3683+ }
3684+
3685+ pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
3686+ for (elf.relocs.items[@intFromEnum(si.get(elf).loc_relocs)..]) |*reloc| {
3687+ if (reloc.loc != si) break;
3688+ reloc.apply(elf);
3689+ }
3690+ }
3691+
3692+ pub fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
3693+ var ri = si.get(elf).target_relocs;
3694+ while (ri != .none) {
3695+ const reloc = ri.get(elf);
3696+ assert(reloc.target == si);
3697+ reloc.apply(elf);
3698+ ri = reloc.next;
3699+ }
3700+ }
3701+
3702+ pub fn deleteLocationRelocs(si: Symbol.Index, elf: *Elf) void {
3703+ const sym = si.get(elf);
3704+ for (elf.relocs.items[@intFromEnum(sym.loc_relocs)..]) |*reloc| {
3705+ if (reloc.loc != si) break;
3706+ reloc.delete(elf);
3707+ }
3708+ sym.loc_relocs = .none;
3709+ }
3710+ };
3711+
3712+ comptime {
3713+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Symbol) == 16);
3714+ }
3715+};
3716+
3717+pub const Reloc = extern struct {
3718+ type: Reloc.Type,
3719+ prev: Reloc.Index,
3720+ next: Reloc.Index,
3721+ loc: Symbol.Index,
3722+ target: Symbol.Index,
3723+ unused: u32,
3724+ offset: u64,
3725+ addend: i64,
3726+
3727+ pub const Type = extern union {
3728+ x86_64: std.elf.R_X86_64,
3729+ aarch64: std.elf.R_AARCH64,
3730+ riscv: std.elf.R_RISCV,
3731+ ppc64: std.elf.R_PPC64,
3732+ };
3733+
3734+ pub const Index = enum(u32) {
3735+ none = std.math.maxInt(u32),
3736+ _,
3737+
3738+ pub fn get(si: Reloc.Index, elf: *Elf) *Reloc {
3739+ return &elf.relocs.items[@intFromEnum(si)];
3740+ }
3741+ };
3742+
3743+ pub fn apply(reloc: *const Reloc, elf: *Elf) void {
3744+ const target_endian = elf.endian();
3745+ switch (reloc.loc.get(elf).ni) {
3746+ .none => return,
3747+ else => |ni| if (ni.hasMoved(&elf.mf)) return,
3748+ }
3749+ switch (reloc.target.get(elf).ni) {
3750+ .none => return,
3751+ else => |ni| if (ni.hasMoved(&elf.mf)) return,
3752+ }
3753+ switch (elf.shdrSlice()) {
3754+ inline else => |shdr, class| {
3755+ const sym = @field(elf.symSlice(), @tagName(class));
3756+ const loc_sym = &sym[@intFromEnum(reloc.loc)];
3757+ const loc_shndx =
3758+ std.mem.toNative(@TypeOf(loc_sym.shndx), loc_sym.shndx, target_endian);
3759+ assert(loc_shndx != std.elf.SHN_UNDEF);
3760+ const loc_sh = &shdr[loc_shndx];
3761+ const loc_value = std.mem.toNative(
3762+ @TypeOf(loc_sym.value),
3763+ loc_sym.value,
3764+ target_endian,
3765+ ) + reloc.offset;
3766+ const loc_sh_addr =
3767+ std.mem.toNative(@TypeOf(loc_sh.addr), loc_sh.addr, target_endian);
3768+ const loc_sh_offset =
3769+ std.mem.toNative(@TypeOf(loc_sh.offset), loc_sh.offset, target_endian);
3770+ const loc_file_offset: usize = @intCast(loc_value - loc_sh_addr + loc_sh_offset);
3771+ const target_sym = &sym[@intFromEnum(reloc.target)];
3772+ const target_value = std.mem.toNative(
3773+ @TypeOf(target_sym.value),
3774+ target_sym.value,
3775+ target_endian,
3776+ ) +% @as(u64, @bitCast(reloc.addend));
3777+ switch (elf.ehdrField(.machine)) {
3778+ else => |machine| @panic(@tagName(machine)),
3779+ .X86_64 => switch (reloc.type.x86_64) {
3780+ else => |kind| @panic(@tagName(kind)),
3781+ .@"64" => std.mem.writeInt(
3782+ u64,
3783+ elf.mf.contents[loc_file_offset..][0..8],
3784+ target_value,
3785+ target_endian,
3786+ ),
3787+ .PC32 => std.mem.writeInt(
3788+ i32,
3789+ elf.mf.contents[loc_file_offset..][0..4],
3790+ @intCast(@as(i64, @bitCast(target_value -% loc_value))),
3791+ target_endian,
3792+ ),
3793+ .@"32" => std.mem.writeInt(
3794+ u32,
3795+ elf.mf.contents[loc_file_offset..][0..4],
3796+ @intCast(target_value),
3797+ target_endian,
3798+ ),
3799+ .TPOFF32 => {
3800+ const phdr = @field(elf.phdrSlice(), @tagName(class));
3801+ const ph = &phdr[4];
3802+ assert(std.mem.toNative(
3803+ @TypeOf(ph.type),
3804+ ph.type,
3805+ target_endian,
3806+ ) == std.elf.PT_TLS);
3807+ std.mem.writeInt(
3808+ i32,
3809+ elf.mf.contents[loc_file_offset..][0..4],
3810+ @intCast(@as(i64, @bitCast(target_value -% std.mem.toNative(
3811+ @TypeOf(ph.memsz),
3812+ ph.memsz,
3813+ target_endian,
3814+ )))),
3815+ target_endian,
3816+ );
3817+ },
3818+ },
3819+ }
3820+ },
3821+ }
3822+ }
3823+
3824+ pub fn delete(reloc: *Reloc, elf: *Elf) void {
3825+ switch (reloc.prev) {
3826+ .none => {
3827+ const target = reloc.target.get(elf);
3828+ assert(target.target_relocs.get(elf) == reloc);
3829+ target.target_relocs = reloc.next;
3830+ },
3831+ else => |prev| prev.get(elf).next = reloc.next,
3832+ }
3833+ switch (reloc.next) {
3834+ .none => {},
3835+ else => |next| next.get(elf).prev = reloc.prev,
3836+ }
3837+ reloc.* = undefined;
3838+ }
3839+
3840+ comptime {
3841+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
3842+ }
3843+};
3844+
3845+pub fn open(
3846+ arena: std.mem.Allocator,
3847+ comp: *Compilation,
3848+ path: std.Build.Cache.Path,
3849+ options: link.File.OpenOptions,
3850+) !*Elf {
3851+ return create(arena, comp, path, options);
3852+}
3853+pub fn createEmpty(
3854+ arena: std.mem.Allocator,
3855+ comp: *Compilation,
3856+ path: std.Build.Cache.Path,
3857+ options: link.File.OpenOptions,
3858+) !*Elf {
3859+ return create(arena, comp, path, options);
3860+}
3861+fn create(
3862+ arena: std.mem.Allocator,
3863+ comp: *Compilation,
3864+ path: std.Build.Cache.Path,
3865+ options: link.File.OpenOptions,
3866+) !*Elf {
3867+ _ = options;
3868+ const target = &comp.root_mod.resolved_target.result;
3869+ assert(target.ofmt == .elf);
3870+ const class: std.elf.CLASS = switch (target.ptrBitWidth()) {
3871+ 0...32 => .@"32",
3872+ 33...64 => .@"64",
3873+ else => return error.UnsupportedELFArchitecture,
3874+ };
3875+ const data: std.elf.DATA = switch (target.cpu.arch.endian()) {
3876+ .little => .@"2LSB",
3877+ .big => .@"2MSB",
3878+ };
3879+ const osabi: std.elf.OSABI = switch (target.os.tag) {
3880+ else => .NONE,
3881+ .freestanding, .other => .STANDALONE,
3882+ .netbsd => .NETBSD,
3883+ .solaris => .SOLARIS,
3884+ .aix => .AIX,
3885+ .freebsd => .FREEBSD,
3886+ .cuda => .CUDA,
3887+ .amdhsa => .AMDGPU_HSA,
3888+ .amdpal => .AMDGPU_PAL,
3889+ .mesa3d => .AMDGPU_MESA3D,
3890+ };
3891+ const @"type": std.elf.ET = switch (comp.config.output_mode) {
3892+ .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
3893+ .Lib => switch (comp.config.link_mode) {
3894+ .static => .REL,
3895+ .dynamic => .DYN,
3896+ },
3897+ .Obj => .REL,
3898+ };
3899+ const machine: std.elf.EM = switch (target.cpu.arch) {
3900+ .spirv32, .spirv64, .wasm32, .wasm64 => .NONE,
3901+ .sparc => .SPARC,
3902+ .x86 => .@"386",
3903+ .m68k => .@"68K",
3904+ .mips, .mipsel, .mips64, .mips64el => .MIPS,
3905+ .powerpc, .powerpcle => .PPC,
3906+ .powerpc64, .powerpc64le => .PPC64,
3907+ .s390x => .S390,
3908+ .arm, .armeb, .thumb, .thumbeb => .ARM,
3909+ .hexagon => .SH,
3910+ .sparc64 => .SPARCV9,
3911+ .arc => .ARC,
3912+ .x86_64 => .X86_64,
3913+ .or1k => .OR1K,
3914+ .xtensa => .XTENSA,
3915+ .msp430 => .MSP430,
3916+ .avr => .AVR,
3917+ .nvptx, .nvptx64 => .CUDA,
3918+ .kalimba => .CSR_KALIMBA,
3919+ .aarch64, .aarch64_be => .AARCH64,
3920+ .xcore => .XCORE,
3921+ .amdgcn => .AMDGPU,
3922+ .riscv32, .riscv32be, .riscv64, .riscv64be => .RISCV,
3923+ .lanai => .LANAI,
3924+ .bpfel, .bpfeb => .BPF,
3925+ .ve => .VE,
3926+ .csky => .CSKY,
3927+ .loongarch32, .loongarch64 => .LOONGARCH,
3928+ .propeller => if (target.cpu.has(.propeller, .p2)) .PROPELLER2 else .PROPELLER,
3929+ };
3930+ const maybe_interp = switch (comp.config.output_mode) {
3931+ .Exe, .Lib => switch (comp.config.link_mode) {
3932+ .static => null,
3933+ .dynamic => target.dynamic_linker.get(),
3934+ },
3935+ .Obj => null,
3936+ };
3937+
3938+ const elf = try arena.create(Elf);
3939+ const file = try path.root_dir.handle.createFile(path.sub_path, .{
3940+ .read = true,
3941+ .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
3942+ });
3943+ errdefer file.close();
3944+ elf.* = .{
3945+ .base = .{
3946+ .tag = .elf2,
3947+
3948+ .comp = comp,
3949+ .emit = path,
3950+
3951+ .file = file,
3952+ .gc_sections = false,
3953+ .print_gc_sections = false,
3954+ .build_id = .none,
3955+ .allow_shlib_undefined = false,
3956+ .stack_size = 0,
3957+ },
3958+ .mf = try .init(file, comp.gpa),
3959+ .nodes = .empty,
3960+ .symtab = .empty,
3961+ .shstrtab = .{
3962+ .map = .empty,
3963+ .size = 1,
3964+ },
3965+ .strtab = .{
3966+ .map = .empty,
3967+ .size = 1,
3968+ },
3969+ .globals = .empty,
3970+ .navs = .empty,
3971+ .uavs = .empty,
3972+ .lazy = .initFill(.{
3973+ .map = .empty,
3974+ .pending_index = 0,
3975+ }),
3976+ .pending_uavs = .empty,
3977+ .relocs = .empty,
3978+ .entry_hack = .null,
3979+ };
3980+ errdefer elf.deinit();
3981+
3982+ switch (class) {
3983+ .NONE, _ => unreachable,
3984+ inline .@"32", .@"64" => |ct_class| try elf.initHeaders(
3985+ ct_class,
3986+ data,
3987+ osabi,
3988+ @"type",
3989+ machine,
3990+ maybe_interp,
3991+ ),
3992+ }
3993+
3994+ return elf;
3995+}
3996+
3997+pub fn deinit(elf: *Elf) void {
3998+ const gpa = elf.base.comp.gpa;
3999+ elf.mf.deinit(gpa);
4000+ elf.nodes.deinit(gpa);
4001+ elf.symtab.deinit(gpa);
4002+ elf.shstrtab.map.deinit(gpa);
4003+ elf.strtab.map.deinit(gpa);
4004+ elf.globals.deinit(gpa);
4005+ elf.navs.deinit(gpa);
4006+ elf.uavs.deinit(gpa);
4007+ for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
4008+ elf.pending_uavs.deinit(gpa);
4009+ elf.relocs.deinit(gpa);
4010+ elf.* = undefined;
4011+}
4012+
4013+fn initHeaders(
4014+ elf: *Elf,
4015+ comptime class: std.elf.CLASS,
4016+ data: std.elf.DATA,
4017+ osabi: std.elf.OSABI,
4018+ @"type": std.elf.ET,
4019+ machine: std.elf.EM,
4020+ maybe_interp: ?[]const u8,
4021+) !void {
4022+ const comp = elf.base.comp;
4023+ const gpa = comp.gpa;
4024+ const ElfN = switch (class) {
4025+ .NONE, _ => comptime unreachable,
4026+ .@"32" => std.elf.Elf32,
4027+ .@"64" => std.elf.Elf64,
4028+ };
4029+ const addr_align: std.mem.Alignment = comptime .fromByteUnits(@sizeOf(ElfN.Addr));
4030+ const target_endian: std.builtin.Endian = switch (data) {
4031+ .NONE, _ => unreachable,
4032+ .@"2LSB" => .little,
4033+ .@"2MSB" => .big,
4034+ };
4035+
4036+ var phnum: u32 = 0;
4037+ const phdr_phndx = phnum;
4038+ phnum += 1;
4039+ const interp_phndx = if (maybe_interp) |_| phndx: {
4040+ defer phnum += 1;
4041+ break :phndx phnum;
4042+ } else undefined;
4043+ const rodata_phndx = phnum;
4044+ phnum += 1;
4045+ const text_phndx = phnum;
4046+ phnum += 1;
4047+ const data_phndx = phnum;
4048+ phnum += 1;
4049+ const tls_phndx = if (comp.config.any_non_single_threaded) phndx: {
4050+ defer phnum += 1;
4051+ break :phndx phnum;
4052+ } else undefined;
4053+
4054+ try elf.nodes.ensureTotalCapacity(gpa, Node.known_count);
4055+ elf.nodes.appendAssumeCapacity(.file);
4056+
4057+ const seg_rodata_ni = Node.known.seg_rodata;
4058+ assert(seg_rodata_ni == try elf.mf.addOnlyChildNode(gpa, .root, .{
4059+ .alignment = elf.mf.flags.block_size,
4060+ .fixed = true,
4061+ .moved = true,
4062+ }));
4063+ elf.nodes.appendAssumeCapacity(.{ .segment = rodata_phndx });
4064+
4065+ const ehdr_ni = Node.known.ehdr;
4066+ assert(ehdr_ni == try elf.mf.addOnlyChildNode(gpa, seg_rodata_ni, .{
4067+ .size = @sizeOf(ElfN.Ehdr),
4068+ .alignment = addr_align,
4069+ .fixed = true,
4070+ }));
4071+ elf.nodes.appendAssumeCapacity(.ehdr);
4072+
4073+ const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(ehdr_ni.slice(&elf.mf)));
4074+ const EI = std.elf.EI;
4075+ @memcpy(ehdr.ident[0..std.elf.MAGIC.len], std.elf.MAGIC);
4076+ ehdr.ident[EI.CLASS] = @intFromEnum(class);
4077+ ehdr.ident[EI.DATA] = @intFromEnum(data);
4078+ ehdr.ident[EI.VERSION] = 1;
4079+ ehdr.ident[EI.OSABI] = @intFromEnum(osabi);
4080+ ehdr.ident[EI.ABIVERSION] = 0;
4081+ @memset(ehdr.ident[EI.PAD..], 0);
4082+ ehdr.type = @"type";
4083+ ehdr.machine = machine;
4084+ ehdr.version = 1;
4085+ ehdr.entry = 0;
4086+ ehdr.phoff = 0;
4087+ ehdr.shoff = 0;
4088+ ehdr.flags = 0;
4089+ ehdr.ehsize = @sizeOf(ElfN.Ehdr);
4090+ ehdr.phentsize = @sizeOf(ElfN.Phdr);
4091+ ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
4092+ ehdr.shentsize = @sizeOf(ElfN.Shdr);
4093+ ehdr.shnum = 1;
4094+ ehdr.shstrndx = 0;
4095+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
4096+
4097+ const phdr_ni = Node.known.phdr;
4098+ assert(phdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
4099+ .size = @sizeOf(ElfN.Phdr) * phnum,
4100+ .alignment = addr_align,
4101+ .moved = true,
4102+ .resized = true,
4103+ }));
4104+ elf.nodes.appendAssumeCapacity(.{ .segment = phdr_phndx });
4105+
4106+ const shdr_ni = Node.known.shdr;
4107+ assert(shdr_ni == try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
4108+ .size = @sizeOf(ElfN.Shdr),
4109+ .alignment = addr_align,
4110+ }));
4111+ elf.nodes.appendAssumeCapacity(.shdr);
4112+
4113+ const seg_text_ni = Node.known.seg_text;
4114+ assert(seg_text_ni == try elf.mf.addLastChildNode(gpa, .root, .{
4115+ .alignment = elf.mf.flags.block_size,
4116+ .moved = true,
4117+ }));
4118+ elf.nodes.appendAssumeCapacity(.{ .segment = text_phndx });
4119+
4120+ const seg_data_ni = Node.known.seg_data;
4121+ assert(seg_data_ni == try elf.mf.addLastChildNode(gpa, .root, .{
4122+ .alignment = elf.mf.flags.block_size,
4123+ .moved = true,
4124+ }));
4125+ elf.nodes.appendAssumeCapacity(.{ .segment = data_phndx });
4126+
4127+ assert(elf.nodes.len == Node.known_count);
4128+
4129+ {
4130+ const phdr: []ElfN.Phdr = @ptrCast(@alignCast(phdr_ni.slice(&elf.mf)));
4131+ const ph_phdr = &phdr[phdr_phndx];
4132+ ph_phdr.* = .{
4133+ .type = std.elf.PT_PHDR,
4134+ .offset = 0,
4135+ .vaddr = 0,
4136+ .paddr = 0,
4137+ .filesz = 0,
4138+ .memsz = 0,
4139+ .flags = .{ .R = true },
4140+ .@"align" = @intCast(phdr_ni.alignment(&elf.mf).toByteUnits()),
4141+ };
4142+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
4143+
4144+ if (maybe_interp) |_| {
4145+ const ph_interp = &phdr[interp_phndx];
4146+ ph_interp.* = .{
4147+ .type = std.elf.PT_INTERP,
4148+ .offset = 0,
4149+ .vaddr = 0,
4150+ .paddr = 0,
4151+ .filesz = 0,
4152+ .memsz = 0,
4153+ .flags = .{ .R = true },
4154+ .@"align" = 1,
4155+ };
4156+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
4157+ }
4158+
4159+ const ph_rodata = &phdr[rodata_phndx];
4160+ ph_rodata.* = .{
4161+ .type = std.elf.PT_NULL,
4162+ .offset = 0,
4163+ .vaddr = 0,
4164+ .paddr = 0,
4165+ .filesz = 0,
4166+ .memsz = 0,
4167+ .flags = .{ .R = true },
4168+ .@"align" = @intCast(seg_rodata_ni.alignment(&elf.mf).toByteUnits()),
4169+ };
4170+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
4171+
4172+ const ph_text = &phdr[text_phndx];
4173+ ph_text.* = .{
4174+ .type = std.elf.PT_NULL,
4175+ .offset = 0,
4176+ .vaddr = 0,
4177+ .paddr = 0,
4178+ .filesz = 0,
4179+ .memsz = 0,
4180+ .flags = .{ .R = true, .X = true },
4181+ .@"align" = @intCast(seg_text_ni.alignment(&elf.mf).toByteUnits()),
4182+ };
4183+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
4184+
4185+ const ph_data = &phdr[data_phndx];
4186+ ph_data.* = .{
4187+ .type = std.elf.PT_NULL,
4188+ .offset = 0,
4189+ .vaddr = 0,
4190+ .paddr = 0,
4191+ .filesz = 0,
4192+ .memsz = 0,
4193+ .flags = .{ .R = true, .W = true },
4194+ .@"align" = @intCast(seg_data_ni.alignment(&elf.mf).toByteUnits()),
4195+ };
4196+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
4197+
4198+ if (comp.config.any_non_single_threaded) {
4199+ const ph_tls = &phdr[tls_phndx];
4200+ ph_tls.* = .{
4201+ .type = std.elf.PT_TLS,
4202+ .offset = 0,
4203+ .vaddr = 0,
4204+ .paddr = 0,
4205+ .filesz = 0,
4206+ .memsz = 0,
4207+ .flags = .{ .R = true },
4208+ .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
4209+ };
4210+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
4211+ }
4212+
4213+ const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(shdr_ni.slice(&elf.mf)));
4214+ sh_null.* = .{
4215+ .name = try elf.string(.shstrtab, ""),
4216+ .type = std.elf.SHT_NULL,
4217+ .flags = .{ .shf = .{} },
4218+ .addr = 0,
4219+ .offset = 0,
4220+ .size = 0,
4221+ .link = 0,
4222+ .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
4223+ .addralign = 0,
4224+ .entsize = 0,
4225+ };
4226+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
4227+ }
4228+
4229+ try elf.symtab.ensureTotalCapacity(gpa, 1);
4230+ elf.symtab.addOneAssumeCapacity().* = .{
4231+ .ni = .none,
4232+ .loc_relocs = .none,
4233+ .target_relocs = .none,
4234+ .unused = 0,
4235+ };
4236+ assert(try elf.addSection(seg_rodata_ni, .{
4237+ .type = std.elf.SHT_SYMTAB,
4238+ .addralign = addr_align,
4239+ .entsize = @sizeOf(ElfN.Sym),
4240+ }) == .symtab);
4241+ const symtab: *ElfN.Sym = @ptrCast(@alignCast(Symbol.Index.symtab.node(elf).slice(&elf.mf)));
4242+ symtab.* = .{
4243+ .name = try elf.string(.strtab, ""),
4244+ .value = 0,
4245+ .size = 0,
4246+ .info = .{
4247+ .type = .NOTYPE,
4248+ .bind = .LOCAL,
4249+ },
4250+ .other = .{
4251+ .visibility = .DEFAULT,
4252+ },
4253+ .shndx = std.elf.SHN_UNDEF,
4254+ };
4255+ ehdr.shstrndx = ehdr.shnum;
4256+ assert(try elf.addSection(seg_rodata_ni, .{
4257+ .type = std.elf.SHT_STRTAB,
4258+ .addralign = elf.mf.flags.block_size,
4259+ .entsize = 1,
4260+ }) == .shstrtab);
4261+ assert(try elf.addSection(seg_rodata_ni, .{
4262+ .type = std.elf.SHT_STRTAB,
4263+ .addralign = elf.mf.flags.block_size,
4264+ .entsize = 1,
4265+ }) == .strtab);
4266+ try elf.renameSection(.symtab, ".symtab");
4267+ try elf.renameSection(.shstrtab, ".shstrtab");
4268+ try elf.renameSection(.strtab, ".strtab");
4269+ try elf.linkSections(.symtab, .strtab);
4270+ Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[0] = 0;
4271+ Symbol.Index.strtab.node(elf).slice(&elf.mf)[0] = 0;
4272+
4273+ assert(try elf.addSection(seg_rodata_ni, .{
4274+ .name = ".rodata",
4275+ .flags = .{ .ALLOC = true },
4276+ .addralign = elf.mf.flags.block_size,
4277+ }) == .rodata);
4278+ assert(try elf.addSection(seg_text_ni, .{
4279+ .name = ".text",
4280+ .flags = .{ .ALLOC = true, .EXECINSTR = true },
4281+ .addralign = elf.mf.flags.block_size,
4282+ }) == .text);
4283+ assert(try elf.addSection(seg_data_ni, .{
4284+ .name = ".data",
4285+ .flags = .{ .WRITE = true, .ALLOC = true },
4286+ .addralign = elf.mf.flags.block_size,
4287+ }) == .data);
4288+ if (comp.config.any_non_single_threaded) {
4289+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
4290+ const seg_tls_ni = try elf.mf.addLastChildNode(gpa, seg_data_ni, .{
4291+ .alignment = elf.mf.flags.block_size,
4292+ .moved = true,
4293+ });
4294+ elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
4295+
4296+ assert(try elf.addSection(seg_tls_ni, .{
4297+ .name = ".tdata",
4298+ .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4299+ .addralign = elf.mf.flags.block_size,
4300+ }) == .tdata);
4301+ }
4302+ if (maybe_interp) |interp| {
4303+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
4304+ const seg_interp_ni = try elf.mf.addLastChildNode(gpa, seg_rodata_ni, .{
4305+ .size = interp.len + 1,
4306+ .moved = true,
4307+ .resized = true,
4308+ });
4309+ elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
4310+
4311+ const sec_interp_si = try elf.addSection(seg_interp_ni, .{
4312+ .name = ".interp",
4313+ .size = @intCast(interp.len + 1),
4314+ .flags = .{ .ALLOC = true },
4315+ });
4316+ const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
4317+ @memcpy(sec_interp[0..interp.len], interp);
4318+ sec_interp[interp.len] = 0;
4319+ }
4320+}
4321+
4322+fn getNode(elf: *Elf, ni: MappedFile.Node.Index) Node {
4323+ return elf.nodes.get(@intFromEnum(ni));
4324+}
4325+
4326+pub const EhdrPtr = union(std.elf.CLASS) {
4327+ NONE: noreturn,
4328+ @"32": *std.elf.Elf32.Ehdr,
4329+ @"64": *std.elf.Elf64.Ehdr,
4330+};
4331+pub fn ehdrPtr(elf: *Elf) EhdrPtr {
4332+ const slice = Node.known.ehdr.slice(&elf.mf);
4333+ return switch (elf.identClass()) {
4334+ .NONE, _ => unreachable,
4335+ inline .@"32", .@"64" => |class| @unionInit(
4336+ EhdrPtr,
4337+ @tagName(class),
4338+ @ptrCast(@alignCast(slice)),
4339+ ),
4340+ };
4341+}
4342+pub fn ehdrField(
4343+ elf: *Elf,
4344+ comptime field: enum { type, machine },
4345+) @FieldType(std.elf.Elf32.Ehdr, @tagName(field)) {
4346+ const Field = @FieldType(std.elf.Elf32.Ehdr, @tagName(field));
4347+ comptime assert(@FieldType(std.elf.Elf64.Ehdr, @tagName(field)) == Field);
4348+ return @enumFromInt(std.mem.toNative(
4349+ @typeInfo(Field).@"enum".tag_type,
4350+ @intFromEnum(switch (elf.ehdrPtr()) {
4351+ inline else => |ehdr| @field(ehdr, @tagName(field)),
4352+ }),
4353+ elf.endian(),
4354+ ));
4355+}
4356+
4357+pub fn identClass(elf: *Elf) std.elf.CLASS {
4358+ return @enumFromInt(elf.mf.contents[std.elf.EI.CLASS]);
4359+}
4360+
4361+pub fn identData(elf: *Elf) std.elf.DATA {
4362+ return @enumFromInt(elf.mf.contents[std.elf.EI.DATA]);
4363+}
4364+fn endianForData(data: std.elf.DATA) std.builtin.Endian {
4365+ return switch (data) {
4366+ .NONE, _ => unreachable,
4367+ .@"2LSB" => .little,
4368+ .@"2MSB" => .big,
4369+ };
4370+}
4371+pub fn endian(elf: *Elf) std.builtin.Endian {
4372+ return endianForData(elf.identData());
4373+}
4374+
4375+fn baseAddrForType(@"type": std.elf.ET) u64 {
4376+ return switch (@"type") {
4377+ else => 0,
4378+ .EXEC => 0x1000000,
4379+ };
4380+}
4381+pub fn baseAddr(elf: *Elf) u64 {
4382+ return baseAddrForType(elf.ehdrField(.type));
4383+}
4384+
4385+pub const PhdrSlice = union(std.elf.CLASS) {
4386+ NONE: noreturn,
4387+ @"32": []std.elf.Elf32.Phdr,
4388+ @"64": []std.elf.Elf64.Phdr,
4389+};
4390+pub fn phdrSlice(elf: *Elf) PhdrSlice {
4391+ const slice = Node.known.phdr.slice(&elf.mf);
4392+ return switch (elf.identClass()) {
4393+ .NONE, _ => unreachable,
4394+ inline .@"32", .@"64" => |class| @unionInit(
4395+ PhdrSlice,
4396+ @tagName(class),
4397+ @ptrCast(@alignCast(slice)),
4398+ ),
4399+ };
4400+}
4401+
4402+pub const ShdrSlice = union(std.elf.CLASS) {
4403+ NONE: noreturn,
4404+ @"32": []std.elf.Elf32.Shdr,
4405+ @"64": []std.elf.Elf64.Shdr,
4406+};
4407+pub fn shdrSlice(elf: *Elf) ShdrSlice {
4408+ const slice = Node.known.shdr.slice(&elf.mf);
4409+ return switch (elf.identClass()) {
4410+ .NONE, _ => unreachable,
4411+ inline .@"32", .@"64" => |class| @unionInit(
4412+ ShdrSlice,
4413+ @tagName(class),
4414+ @ptrCast(@alignCast(slice)),
4415+ ),
4416+ };
4417+}
4418+
4419+pub const SymSlice = union(std.elf.CLASS) {
4420+ NONE: noreturn,
4421+ @"32": []std.elf.Elf32.Sym,
4422+ @"64": []std.elf.Elf64.Sym,
4423+};
4424+pub fn symSlice(elf: *Elf) SymSlice {
4425+ const slice = Symbol.Index.symtab.node(elf).slice(&elf.mf);
4426+ return switch (elf.identClass()) {
4427+ .NONE, _ => unreachable,
4428+ inline .@"32", .@"64" => |class| @unionInit(
4429+ SymSlice,
4430+ @tagName(class),
4431+ @ptrCast(@alignCast(slice)),
4432+ ),
4433+ };
4434+}
4435+
4436+pub const SymPtr = union(std.elf.CLASS) {
4437+ NONE: noreturn,
4438+ @"32": *std.elf.Elf32.Sym,
4439+ @"64": *std.elf.Elf64.Sym,
4440+};
4441+pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
4442+ return switch (elf.symSlice()) {
4443+ inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]),
4444+ };
4445+}
4446+
4447+fn addSymbolAssumeCapacity(elf: *Elf) !Symbol.Index {
4448+ defer elf.symtab.addOneAssumeCapacity().* = .{
4449+ .ni = .none,
4450+ .loc_relocs = .none,
4451+ .target_relocs = .none,
4452+ .unused = 0,
4453+ };
4454+ return @enumFromInt(elf.symtab.items.len);
4455+}
4456+
4457+fn initSymbolAssumeCapacity(elf: *Elf, opts: Symbol.Index.InitOptions) !Symbol.Index {
4458+ const si = try elf.addSymbolAssumeCapacity();
4459+ try si.init(elf, opts);
4460+ return si;
4461+}
4462+
4463+pub fn globalSymbol(
4464+ elf: *Elf,
4465+ opts: struct {
4466+ name: []const u8,
4467+ type: std.elf.STT,
4468+ bind: std.elf.STB = .GLOBAL,
4469+ visibility: std.elf.STV = .DEFAULT,
4470+ },
4471+) !Symbol.Index {
4472+ const gpa = elf.base.comp.gpa;
4473+ try elf.symtab.ensureUnusedCapacity(gpa, 1);
4474+ const sym_gop = try elf.globals.getOrPut(gpa, try elf.string(.strtab, opts.name));
4475+ if (!sym_gop.found_existing) sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
4476+ .name = opts.name,
4477+ .type = opts.type,
4478+ .bind = opts.bind,
4479+ .visibility = opts.visibility,
4480+ });
4481+ return sym_gop.value_ptr.*;
4482+}
4483+
4484+fn navType(
4485+ ip: *const InternPool,
4486+ nav_status: @FieldType(InternPool.Nav, "status"),
4487+ any_non_single_threaded: bool,
4488+) std.elf.STT {
4489+ return switch (nav_status) {
4490+ .unresolved => unreachable,
4491+ .type_resolved => |tr| if (any_non_single_threaded and tr.is_threadlocal)
4492+ .TLS
4493+ else if (ip.isFunctionType(tr.type))
4494+ .FUNC
4495+ else
4496+ .OBJECT,
4497+ .fully_resolved => |fr| switch (ip.indexToKey(fr.val)) {
4498+ else => .OBJECT,
4499+ .variable => |variable| if (any_non_single_threaded and variable.is_threadlocal)
4500+ .TLS
4501+ else
4502+ .OBJECT,
4503+ .@"extern" => |@"extern"| if (any_non_single_threaded and @"extern".is_threadlocal)
4504+ .TLS
4505+ else if (ip.isFunctionType(@"extern".ty))
4506+ .FUNC
4507+ else
4508+ .OBJECT,
4509+ .func => .FUNC,
4510+ },
4511+ };
4512+}
4513+pub fn navSymbol(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
4514+ const gpa = zcu.gpa;
4515+ const ip = &zcu.intern_pool;
4516+ const nav = ip.getNav(nav_index);
4517+ if (nav.getExtern(ip)) |@"extern"| return elf.globalSymbol(.{
4518+ .name = @"extern".name.toSlice(ip),
4519+ .type = navType(ip, nav.status, elf.base.comp.config.any_non_single_threaded),
4520+ .bind = switch (@"extern".linkage) {
4521+ .internal => .LOCAL,
4522+ .strong => .GLOBAL,
4523+ .weak => .WEAK,
4524+ .link_once => return error.LinkOnceUnsupported,
4525+ },
4526+ .visibility = switch (@"extern".visibility) {
4527+ .default => .DEFAULT,
4528+ .hidden => .HIDDEN,
4529+ .protected => .PROTECTED,
4530+ },
4531+ });
4532+ try elf.symtab.ensureUnusedCapacity(gpa, 1);
4533+ const sym_gop = try elf.navs.getOrPut(gpa, nav_index);
4534+ if (!sym_gop.found_existing) {
4535+ sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
4536+ .name = nav.fqn.toSlice(ip),
4537+ .type = navType(ip, nav.status, elf.base.comp.config.any_non_single_threaded),
4538+ });
4539+ }
4540+ return sym_gop.value_ptr.*;
4541+}
4542+
4543+pub fn uavSymbol(elf: *Elf, uav_val: InternPool.Index) !Symbol.Index {
4544+ const gpa = elf.base.comp.gpa;
4545+ try elf.symtab.ensureUnusedCapacity(gpa, 1);
4546+ const sym_gop = try elf.uavs.getOrPut(gpa, uav_val);
4547+ if (!sym_gop.found_existing)
4548+ sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{ .type = .OBJECT });
4549+ return sym_gop.value_ptr.*;
4550+}
4551+
4552+pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {
4553+ const gpa = elf.base.comp.gpa;
4554+ try elf.symtab.ensureUnusedCapacity(gpa, 1);
4555+ const sym_gop = try elf.lazy.getPtr(lazy.kind).map.getOrPut(gpa, lazy.ty);
4556+ if (!sym_gop.found_existing) {
4557+ sym_gop.value_ptr.* = try elf.initSymbolAssumeCapacity(.{
4558+ .type = switch (lazy.kind) {
4559+ .code => .FUNC,
4560+ .const_data => .OBJECT,
4561+ },
4562+ });
4563+ elf.base.comp.link_lazy_prog_node.increaseEstimatedTotalItems(1);
4564+ }
4565+ return sym_gop.value_ptr.*;
4566+}
4567+
4568+pub fn getNavVAddr(
4569+ elf: *Elf,
4570+ pt: Zcu.PerThread,
4571+ nav: InternPool.Nav.Index,
4572+ reloc_info: link.File.RelocInfo,
4573+) !u64 {
4574+ return elf.getVAddr(reloc_info, try elf.navSymbol(pt.zcu, nav));
4575+}
4576+
4577+pub fn getUavVAddr(
4578+ elf: *Elf,
4579+ uav: InternPool.Index,
4580+ reloc_info: link.File.RelocInfo,
4581+) !u64 {
4582+ return elf.getVAddr(reloc_info, try elf.uavSymbol(uav));
4583+}
4584+
4585+pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target_si: Symbol.Index) !u64 {
4586+ try elf.addReloc(
4587+ @enumFromInt(reloc_info.parent.atom_index),
4588+ reloc_info.offset,
4589+ target_si,
4590+ reloc_info.addend,
4591+ switch (elf.ehdrField(.machine)) {
4592+ else => unreachable,
4593+ .X86_64 => .{ .x86_64 = switch (elf.identClass()) {
4594+ .NONE, _ => unreachable,
4595+ .@"32" => .@"32",
4596+ .@"64" => .@"64",
4597+ } },
4598+ },
4599+ );
4600+ return 0;
4601+}
4602+
4603+fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
4604+ name: []const u8 = "",
4605+ type: std.elf.Word = std.elf.SHT_NULL,
4606+ size: std.elf.Word = 0,
4607+ flags: std.elf.SHF = .{},
4608+ addralign: std.mem.Alignment = .@"1",
4609+ entsize: std.elf.Word = 0,
4610+}) !Symbol.Index {
4611+ const gpa = elf.base.comp.gpa;
4612+ const target_endian = elf.endian();
4613+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
4614+ try elf.symtab.ensureUnusedCapacity(gpa, 1);
4615+
4616+ const shstrtab_entry = try elf.string(.shstrtab, opts.name);
4617+ const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) {
4618+ inline else => |ehdr| {
4619+ const shentsize = std.mem.toNative(@TypeOf(ehdr.shentsize), ehdr.shentsize, target_endian);
4620+ const shndx = std.mem.toNative(@TypeOf(ehdr.shnum), ehdr.shnum, target_endian);
4621+ const shnum = shndx + 1;
4622+ ehdr.shnum = std.mem.nativeTo(@TypeOf(ehdr.shnum), shnum, target_endian);
4623+ break :shndx .{ shndx, shentsize * shnum };
4624+ },
4625+ };
4626+ try Node.known.shdr.resize(&elf.mf, gpa, shdr_size);
4627+ const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{
4628+ .alignment = opts.addralign,
4629+ .size = opts.size,
4630+ .moved = true,
4631+ });
4632+ const si = try elf.addSymbolAssumeCapacity();
4633+ elf.nodes.appendAssumeCapacity(.{ .section = si });
4634+ si.get(elf).ni = ni;
4635+ try si.init(elf, .{
4636+ .name = opts.name,
4637+ .size = opts.size,
4638+ .type = .SECTION,
4639+ .shndx = shndx,
4640+ });
4641+ switch (elf.shdrSlice()) {
4642+ inline else => |shdr| {
4643+ const sh = &shdr[shndx];
4644+ sh.* = .{
4645+ .name = shstrtab_entry,
4646+ .type = opts.type,
4647+ .flags = .{ .shf = opts.flags },
4648+ .addr = 0,
4649+ .offset = 0,
4650+ .size = opts.size,
4651+ .link = 0,
4652+ .info = 0,
4653+ .addralign = @intCast(opts.addralign.toByteUnits()),
4654+ .entsize = opts.entsize,
4655+ };
4656+ if (target_endian != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
4657+ },
4658+ }
4659+ return si;
4660+}
4661+
4662+fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
4663+ const strtab_entry = try elf.string(.strtab, name);
4664+ const shstrtab_entry = try elf.string(.shstrtab, name);
4665+ const target_endian = elf.endian();
4666+ switch (elf.shdrSlice()) {
4667+ inline else => |shdr, class| {
4668+ const sym = @field(elf.symPtr(si), @tagName(class));
4669+ sym.name = std.mem.nativeTo(@TypeOf(sym.name), strtab_entry, target_endian);
4670+ const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
4671+ const sh = &shdr[shndx];
4672+ sh.name = std.mem.nativeTo(@TypeOf(sh.name), shstrtab_entry, target_endian);
4673+ },
4674+ }
4675+}
4676+
4677+fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void {
4678+ const target_endian = elf.endian();
4679+ switch (elf.shdrSlice()) {
4680+ inline else => |shdr, class| {
4681+ const sym = @field(elf.symPtr(si), @tagName(class));
4682+ const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
4683+ shdr[shndx].link = @field(elf.symPtr(link_si), @tagName(class)).shndx;
4684+ },
4685+ }
4686+}
4687+
4688+fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
4689+ const target_endian = elf.endian();
4690+ const name = Symbol.Index.shstrtab.node(elf).slice(&elf.mf)[name: switch (elf.shdrSlice()) {
4691+ inline else => |shndx, class| {
4692+ const sym = @field(elf.symPtr(si), @tagName(class));
4693+ const sh = &shndx[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
4694+ break :name std.mem.toNative(@TypeOf(sh.name), sh.name, target_endian);
4695+ },
4696+ }..];
4697+ return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
4698+}
4699+
4700+fn string(elf: *Elf, comptime section: enum { shstrtab, strtab }, key: []const u8) !u32 {
4701+ if (key.len == 0) return 0;
4702+ return @field(elf, @tagName(section)).get(
4703+ elf.base.comp.gpa,
4704+ &elf.mf,
4705+ @field(Symbol.Index, @tagName(section)).node(elf),
4706+ key,
4707+ );
4708+}
4709+
4710+pub fn addReloc(
4711+ elf: *Elf,
4712+ loc_si: Symbol.Index,
4713+ offset: u64,
4714+ target_si: Symbol.Index,
4715+ addend: i64,
4716+ @"type": Reloc.Type,
4717+) !void {
4718+ const gpa = elf.base.comp.gpa;
4719+ const target = target_si.get(elf);
4720+ const ri: link.File.Elf2.Reloc.Index = @enumFromInt(elf.relocs.items.len);
4721+ (try elf.relocs.addOne(gpa)).* = .{
4722+ .type = @"type",
4723+ .prev = .none,
4724+ .next = target.target_relocs,
4725+ .loc = loc_si,
4726+ .target = target_si,
4727+ .unused = 0,
4728+ .offset = offset,
4729+ .addend = addend,
4730+ };
4731+ switch (target.target_relocs) {
4732+ .none => {},
4733+ else => |target_ri| target_ri.get(elf).prev = ri,
4734+ }
4735+ target.target_relocs = ri;
4736+}
4737+
4738+pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) void {
4739+ _ = elf;
4740+ _ = prog_node;
4741+}
4742+
4743+pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
4744+ elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
4745+ error.OutOfMemory,
4746+ error.Overflow,
4747+ error.RelocationNotByteAligned,
4748+ => |e| return e,
4749+ else => |e| return elf.base.cgFail(nav_index, "linker failed to update variable: {t}", .{e}),
4750+ };
4751+}
4752+fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
4753+ const comp = elf.base.comp;
4754+ const zcu = pt.zcu;
4755+ const gpa = zcu.gpa;
4756+ const ip = &zcu.intern_pool;
4757+
4758+ const nav = ip.getNav(nav_index);
4759+ const nav_val = nav.status.fully_resolved.val;
4760+ const nav_init, const is_threadlocal = switch (ip.indexToKey(nav_val)) {
4761+ else => .{ nav_val, false },
4762+ .variable => |variable| .{ variable.init, variable.is_threadlocal },
4763+ .@"extern" => return,
4764+ .func => .{ .none, false },
4765+ };
4766+ if (nav_init == .none or !Type.fromInterned(ip.typeOf(nav_init)).hasRuntimeBits(zcu)) return;
4767+
4768+ const si = try elf.navSymbol(zcu, nav_index);
4769+ const ni = ni: {
4770+ const sym = si.get(elf);
4771+ switch (sym.ni) {
4772+ .none => {
4773+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
4774+ const sec_si: Symbol.Index =
4775+ if (is_threadlocal and comp.config.any_non_single_threaded) .tdata else .data;
4776+ const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
4777+ .alignment = pt.navAlignment(nav_index).toStdMem(),
4778+ .moved = true,
4779+ });
4780+ elf.nodes.appendAssumeCapacity(.{ .nav = nav_index });
4781+ sym.ni = ni;
4782+ switch (elf.symPtr(si)) {
4783+ inline else => |sym_ptr, class| sym_ptr.shndx =
4784+ @field(elf.symPtr(sec_si), @tagName(class)).shndx,
4785+ }
4786+ },
4787+ else => si.deleteLocationRelocs(elf),
4788+ }
4789+ assert(sym.loc_relocs == .none);
4790+ sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
4791+ break :ni sym.ni;
4792+ };
4793+
4794+ const size = size: {
4795+ var nw: MappedFile.Node.Writer = undefined;
4796+ ni.writer(&elf.mf, gpa, &nw);
4797+ defer nw.deinit();
4798+ codegen.generateSymbol(
4799+ &elf.base,
4800+ pt,
4801+ zcu.navSrcLoc(nav_index),
4802+ .fromInterned(nav_init),
4803+ &nw.interface,
4804+ .{ .atom_index = @intFromEnum(si) },
4805+ ) catch |err| switch (err) {
4806+ error.WriteFailed => return error.OutOfMemory,
4807+ else => |e| return e,
4808+ };
4809+ break :size nw.interface.end;
4810+ };
4811+
4812+ const target_endian = elf.endian();
4813+ switch (elf.symPtr(si)) {
4814+ inline else => |sym| sym.size =
4815+ std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
4816+ }
4817+ si.applyLocationRelocs(elf);
4818+}
4819+
4820+pub fn lowerUav(
4821+ elf: *Elf,
4822+ pt: Zcu.PerThread,
4823+ uav_val: InternPool.Index,
4824+ uav_align: InternPool.Alignment,
4825+ src_loc: Zcu.LazySrcLoc,
4826+) !codegen.SymbolResult {
4827+ const zcu = pt.zcu;
4828+ const gpa = zcu.gpa;
4829+
4830+ try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
4831+ const si = elf.uavSymbol(uav_val) catch |err| switch (err) {
4832+ error.OutOfMemory => return error.OutOfMemory,
4833+ else => |e| return .{ .fail = try Zcu.ErrorMsg.create(
4834+ gpa,
4835+ src_loc,
4836+ "linker failed to update constant: {s}",
4837+ .{@errorName(e)},
4838+ ) },
4839+ };
4840+ if (switch (si.get(elf).ni) {
4841+ .none => true,
4842+ else => |ni| uav_align.toStdMem().order(ni.alignment(&elf.mf)).compare(.gt),
4843+ }) {
4844+ const gop = elf.pending_uavs.getOrPutAssumeCapacity(uav_val);
4845+ if (gop.found_existing) {
4846+ gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
4847+ } else {
4848+ gop.value_ptr.* = .{
4849+ .alignment = uav_align,
4850+ .src_loc = src_loc,
4851+ };
4852+ elf.base.comp.link_uav_prog_node.increaseEstimatedTotalItems(1);
4853+ }
4854+ }
4855+ return .{ .sym_index = @intFromEnum(si) };
4856+}
4857+
4858+pub fn updateFunc(
4859+ elf: *Elf,
4860+ pt: Zcu.PerThread,
4861+ func_index: InternPool.Index,
4862+ mir: *const codegen.AnyMir,
4863+) !void {
4864+ elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
4865+ error.OutOfMemory,
4866+ error.Overflow,
4867+ error.RelocationNotByteAligned,
4868+ error.CodegenFail,
4869+ => |e| return e,
4870+ else => |e| return elf.base.cgFail(
4871+ pt.zcu.funcInfo(func_index).owner_nav,
4872+ "linker failed to update function: {s}",
4873+ .{@errorName(e)},
4874+ ),
4875+ };
4876+}
4877+fn updateFuncInner(
4878+ elf: *Elf,
4879+ pt: Zcu.PerThread,
4880+ func_index: InternPool.Index,
4881+ mir: *const codegen.AnyMir,
4882+) !void {
4883+ const zcu = pt.zcu;
4884+ const gpa = zcu.gpa;
4885+ const ip = &zcu.intern_pool;
4886+ const func = zcu.funcInfo(func_index);
4887+ const nav = ip.getNav(func.owner_nav);
4888+
4889+ const si = try elf.navSymbol(zcu, func.owner_nav);
4890+ log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), si });
4891+ const ni = ni: {
4892+ const sym = si.get(elf);
4893+ switch (sym.ni) {
4894+ .none => {
4895+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
4896+ const mod = zcu.navFileScope(func.owner_nav).mod.?;
4897+ const target = &mod.resolved_target.result;
4898+ const ni = try elf.mf.addLastChildNode(gpa, Symbol.Index.text.node(elf), .{
4899+ .alignment = switch (nav.status.fully_resolved.alignment) {
4900+ .none => switch (mod.optimize_mode) {
4901+ .Debug,
4902+ .ReleaseSafe,
4903+ .ReleaseFast,
4904+ => target_util.defaultFunctionAlignment(target),
4905+ .ReleaseSmall => target_util.minFunctionAlignment(target),
4906+ },
4907+ else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
4908+ }.toStdMem(),
4909+ .moved = true,
4910+ });
4911+ elf.nodes.appendAssumeCapacity(.{ .nav = func.owner_nav });
4912+ sym.ni = ni;
4913+ switch (elf.symPtr(si)) {
4914+ inline else => |sym_ptr, class| sym_ptr.shndx =
4915+ @field(elf.symPtr(.text), @tagName(class)).shndx,
4916+ }
4917+ },
4918+ else => si.deleteLocationRelocs(elf),
4919+ }
4920+ assert(sym.loc_relocs == .none);
4921+ sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
4922+ break :ni sym.ni;
4923+ };
4924+
4925+ const size = size: {
4926+ var nw: MappedFile.Node.Writer = undefined;
4927+ ni.writer(&elf.mf, gpa, &nw);
4928+ defer nw.deinit();
4929+ codegen.emitFunction(
4930+ &elf.base,
4931+ pt,
4932+ zcu.navSrcLoc(func.owner_nav),
4933+ func_index,
4934+ @intFromEnum(si),
4935+ mir,
4936+ &nw.interface,
4937+ .none,
4938+ ) catch |err| switch (err) {
4939+ error.WriteFailed => return nw.err.?,
4940+ else => |e| return e,
4941+ };
4942+ break :size nw.interface.end;
4943+ };
4944+
4945+ const target_endian = elf.endian();
4946+ switch (elf.symPtr(si)) {
4947+ inline else => |sym| sym.size =
4948+ std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
4949+ }
4950+ si.applyLocationRelocs(elf);
4951+}
4952+
4953+pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) !void {
4954+ const si = elf.lazy.getPtr(.const_data).map.get(.anyerror_type) orelse return;
4955+ elf.flushLazy(pt, .{ .kind = .const_data, .ty = .anyerror_type }, si) catch |err| switch (err) {
4956+ error.OutOfMemory => return error.OutOfMemory,
4957+ error.CodegenFail => return error.LinkFailure,
4958+ else => |e| return elf.base.comp.link_diags.fail("updateErrorData failed {t}", .{e}),
4959+ };
4960+}
4961+
4962+pub fn flush(
4963+ elf: *Elf,
4964+ arena: std.mem.Allocator,
4965+ tid: Zcu.PerThread.Id,
4966+ prog_node: std.Progress.Node,
4967+) !void {
4968+ _ = arena;
4969+ _ = prog_node;
4970+ while (try elf.idle(tid)) {}
4971+}
4972+
4973+pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4974+ const comp = elf.base.comp;
4975+ task: {
4976+ while (elf.pending_uavs.pop()) |pending_uav| {
4977+ const sub_prog_node =
4978+ elf.idleProgNode(
4979+ tid,
4980+ comp.link_uav_prog_node,
4981+ .{ .uav = pending_uav.key },
4982+ );
4983+ defer sub_prog_node.end();
4984+ break :task elf.flushUav(
4985+ .{ .zcu = elf.base.comp.zcu.?, .tid = tid },
4986+ pending_uav.key,
4987+ pending_uav.value.alignment,
4988+ pending_uav.value.src_loc,
4989+ ) catch |err| switch (err) {
4990+ error.OutOfMemory => return error.OutOfMemory,
4991+ else => |e| return elf.base.comp.link_diags.fail(
4992+ "linker failed to lower constant: {t}",
4993+ .{e},
4994+ ),
4995+ };
4996+ }
4997+ var lazy_it = elf.lazy.iterator();
4998+ while (lazy_it.next()) |lazy| for (
4999+ lazy.value.map.keys()[lazy.value.pending_index..],
5000+ lazy.value.map.values()[lazy.value.pending_index..],
5001+ ) |ty, si| {
5002+ lazy.value.pending_index += 1;
5003+ const pt: Zcu.PerThread = .{ .zcu = elf.base.comp.zcu.?, .tid = tid };
5004+ const kind = switch (lazy.key) {
5005+ .code => "code",
5006+ .const_data => "data",
5007+ };
5008+ var name: [std.Progress.Node.max_name_len]u8 = undefined;
5009+ const sub_prog_node = comp.link_lazy_prog_node.start(
5010+ std.fmt.bufPrint(&name, "lazy {s} for {f}", .{
5011+ kind,
5012+ Type.fromInterned(ty).fmt(pt),
5013+ }) catch &name,
5014+ 0,
5015+ );
5016+ defer sub_prog_node.end();
5017+ break :task elf.flushLazy(pt, .{
5018+ .kind = lazy.key,
5019+ .ty = ty,
5020+ }, si) catch |err| switch (err) {
5021+ error.OutOfMemory => return error.OutOfMemory,
5022+ else => |e| return elf.base.comp.link_diags.fail(
5023+ "linker failed to lower lazy {s}: {t}",
5024+ .{ kind, e },
5025+ ),
5026+ };
5027+ };
5028+ while (elf.mf.updates.pop()) |ni| {
5029+ const clean_moved = ni.cleanMoved(&elf.mf);
5030+ const clean_resized = ni.cleanResized(&elf.mf);
5031+ if (clean_moved or clean_resized) {
5032+ const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
5033+ defer sub_prog_node.end();
5034+ if (clean_moved) try elf.flushMoved(ni);
5035+ if (clean_resized) try elf.flushResized(ni);
5036+ break :task;
5037+ } else elf.mf.update_prog_node.completeOne();
5038+ }
5039+ }
5040+ if (elf.pending_uavs.count() > 0) return true;
5041+ for (&elf.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5042+ if (elf.mf.updates.items.len > 0) return true;
5043+ return false;
5044+}
5045+
5046+fn idleProgNode(
5047+ elf: *Elf,
5048+ tid: Zcu.PerThread.Id,
5049+ prog_node: std.Progress.Node,
5050+ node: Node,
5051+) std.Progress.Node {
5052+ var name: [std.Progress.Node.max_name_len]u8 = undefined;
5053+ return prog_node.start(name: switch (node) {
5054+ else => |tag| @tagName(tag),
5055+ .section => |si| elf.sectionName(si),
5056+ .nav => |nav| {
5057+ const ip = &elf.base.comp.zcu.?.intern_pool;
5058+ break :name ip.getNav(nav).fqn.toSlice(ip);
5059+ },
5060+ .uav => |uav| std.fmt.bufPrint(&name, "{f}", .{
5061+ Value.fromInterned(uav).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
5062+ }) catch &name,
5063+ }, 0);
5064+}
5065+
5066+fn flushUav(
5067+ elf: *Elf,
5068+ pt: Zcu.PerThread,
5069+ uav_val: InternPool.Index,
5070+ uav_align: InternPool.Alignment,
5071+ src_loc: Zcu.LazySrcLoc,
5072+) !void {
5073+ const zcu = pt.zcu;
5074+ const gpa = zcu.gpa;
5075+
5076+ const si = try elf.uavSymbol(uav_val);
5077+ const ni = ni: {
5078+ const sym = si.get(elf);
5079+ switch (sym.ni) {
5080+ .none => {
5081+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
5082+ const ni = try elf.mf.addLastChildNode(gpa, Symbol.Index.data.node(elf), .{
5083+ .alignment = uav_align.toStdMem(),
5084+ .moved = true,
5085+ });
5086+ elf.nodes.appendAssumeCapacity(.{ .uav = uav_val });
5087+ sym.ni = ni;
5088+ switch (elf.symPtr(si)) {
5089+ inline else => |sym_ptr, class| sym_ptr.shndx =
5090+ @field(elf.symPtr(.data), @tagName(class)).shndx,
5091+ }
5092+ },
5093+ else => {
5094+ if (sym.ni.alignment(&elf.mf).order(uav_align.toStdMem()).compare(.gte)) return;
5095+ si.deleteLocationRelocs(elf);
5096+ },
5097+ }
5098+ assert(sym.loc_relocs == .none);
5099+ sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
5100+ break :ni sym.ni;
5101+ };
5102+
5103+ const size = size: {
5104+ var nw: MappedFile.Node.Writer = undefined;
5105+ ni.writer(&elf.mf, gpa, &nw);
5106+ defer nw.deinit();
5107+ codegen.generateSymbol(
5108+ &elf.base,
5109+ pt,
5110+ src_loc,
5111+ .fromInterned(uav_val),
5112+ &nw.interface,
5113+ .{ .atom_index = @intFromEnum(si) },
5114+ ) catch |err| switch (err) {
5115+ error.WriteFailed => return error.OutOfMemory,
5116+ else => |e| return e,
5117+ };
5118+ break :size nw.interface.end;
5119+ };
5120+
5121+ const target_endian = elf.endian();
5122+ switch (elf.symPtr(si)) {
5123+ inline else => |sym| sym.size =
5124+ std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
5125+ }
5126+ si.applyLocationRelocs(elf);
5127+}
5128+
5129+fn flushLazy(elf: *Elf, pt: Zcu.PerThread, lazy: link.File.LazySymbol, si: Symbol.Index) !void {
5130+ const zcu = pt.zcu;
5131+ const gpa = zcu.gpa;
5132+
5133+ const ni = ni: {
5134+ const sym = si.get(elf);
5135+ switch (sym.ni) {
5136+ .none => {
5137+ try elf.nodes.ensureUnusedCapacity(gpa, 1);
5138+ const sec_si: Symbol.Index = switch (lazy.kind) {
5139+ .code => .text,
5140+ .const_data => .rodata,
5141+ };
5142+ const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{ .moved = true });
5143+ elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
5144+ .code => .{ .lazy_code = lazy.ty },
5145+ .const_data => .{ .lazy_const_data = lazy.ty },
5146+ });
5147+ sym.ni = ni;
5148+ switch (elf.symPtr(si)) {
5149+ inline else => |sym_ptr, class| sym_ptr.shndx =
5150+ @field(elf.symPtr(sec_si), @tagName(class)).shndx,
5151+ }
5152+ },
5153+ else => si.deleteLocationRelocs(elf),
5154+ }
5155+ assert(sym.loc_relocs == .none);
5156+ sym.loc_relocs = @enumFromInt(elf.relocs.items.len);
5157+ break :ni sym.ni;
5158+ };
5159+
5160+ const size = size: {
5161+ var required_alignment: InternPool.Alignment = .none;
5162+ var nw: MappedFile.Node.Writer = undefined;
5163+ ni.writer(&elf.mf, gpa, &nw);
5164+ defer nw.deinit();
5165+ try codegen.generateLazySymbol(
5166+ &elf.base,
5167+ pt,
5168+ Type.fromInterned(lazy.ty).srcLocOrNull(pt.zcu) orelse .unneeded,
5169+ lazy,
5170+ &required_alignment,
5171+ &nw.interface,
5172+ .none,
5173+ .{ .atom_index = @intFromEnum(si) },
5174+ );
5175+ break :size nw.interface.end;
5176+ };
5177+
5178+ const target_endian = elf.endian();
5179+ switch (elf.symPtr(si)) {
5180+ inline else => |sym| sym.size =
5181+ std.mem.nativeTo(@TypeOf(sym.size), @intCast(size), target_endian),
5182+ }
5183+ si.applyLocationRelocs(elf);
5184+}
5185+
5186+fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5187+ const target_endian = elf.endian();
5188+ const file_offset = ni.fileLocation(&elf.mf, false).offset;
5189+ const node = elf.getNode(ni);
5190+ switch (node) {
5191+ else => |tag| @panic(@tagName(tag)),
5192+ .ehdr => assert(file_offset == 0),
5193+ .shdr => switch (elf.ehdrPtr()) {
5194+ inline else => |ehdr| ehdr.shoff =
5195+ std.mem.nativeTo(@TypeOf(ehdr.shoff), @intCast(file_offset), target_endian),
5196+ },
5197+ .segment => |phndx| switch (elf.phdrSlice()) {
5198+ inline else => |phdr, class| {
5199+ const ph = &phdr[phndx];
5200+ switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
5201+ else => unreachable,
5202+ std.elf.PT_NULL, std.elf.PT_LOAD, std.elf.PT_DYNAMIC, std.elf.PT_INTERP => {},
5203+ std.elf.PT_PHDR => {
5204+ const ehdr = @field(elf.ehdrPtr(), @tagName(class));
5205+ ehdr.phoff =
5206+ std.mem.nativeTo(@TypeOf(ehdr.phoff), @intCast(file_offset), target_endian);
5207+ },
5208+ std.elf.PT_TLS => {},
5209+ }
5210+ ph.offset = std.mem.nativeTo(@TypeOf(ph.offset), @intCast(file_offset), target_endian);
5211+ ph.vaddr = std.mem.nativeTo(
5212+ @TypeOf(ph.vaddr),
5213+ @intCast(elf.baseAddr() + file_offset),
5214+ target_endian,
5215+ );
5216+ ph.paddr = ph.vaddr;
5217+ },
5218+ },
5219+ .section => |si| switch (elf.shdrSlice()) {
5220+ inline else => |shdr, class| {
5221+ const sym = @field(elf.symPtr(si), @tagName(class));
5222+ const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
5223+ const sh = &shdr[shndx];
5224+ const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
5225+ @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
5226+ @bitCast(sh.flags),
5227+ target_endian,
5228+ ));
5229+ if (flags.shf.ALLOC) {
5230+ sym.value = std.mem.nativeTo(
5231+ @TypeOf(sym.value),
5232+ @intCast(elf.baseAddr() + file_offset),
5233+ target_endian,
5234+ );
5235+ sh.addr = sym.value;
5236+ }
5237+ sh.offset = std.mem.nativeTo(@TypeOf(sh.offset), @intCast(file_offset), target_endian);
5238+ },
5239+ },
5240+ .nav, .uav, .lazy_code, .lazy_const_data => {
5241+ const si = switch (node) {
5242+ else => unreachable,
5243+ .nav => |nav| elf.navs.get(nav),
5244+ .uav => |uav| elf.uavs.get(uav),
5245+ .lazy_code => |ty| elf.lazy.getPtr(.code).map.get(ty),
5246+ .lazy_const_data => |ty| elf.lazy.getPtr(.const_data).map.get(ty),
5247+ }.?;
5248+ switch (elf.shdrSlice()) {
5249+ inline else => |shdr, class| {
5250+ const sym = @field(elf.symPtr(si), @tagName(class));
5251+ const sh = &shdr[std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian)];
5252+ const flags: @TypeOf(sh.flags) = @bitCast(std.mem.toNative(
5253+ @typeInfo(@TypeOf(sh.flags)).@"struct".backing_integer.?,
5254+ @bitCast(sh.flags),
5255+ target_endian,
5256+ ));
5257+ const sh_addr = if (flags.shf.TLS)
5258+ 0
5259+ else
5260+ std.mem.toNative(@TypeOf(sh.addr), sh.addr, target_endian);
5261+ const sh_offset = std.mem.toNative(@TypeOf(sh.offset), sh.offset, target_endian);
5262+ sym.value = std.mem.nativeTo(
5263+ @TypeOf(sym.value),
5264+ @intCast(file_offset - sh_offset + sh_addr),
5265+ target_endian,
5266+ );
5267+ if (si == elf.entry_hack) @field(elf.ehdrPtr(), @tagName(class)).entry = sym.value;
5268+ },
5269+ }
5270+ si.applyLocationRelocs(elf);
5271+ si.applyTargetRelocs(elf);
5272+ },
5273+ }
5274+ try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
5275+}
5276+
5277+fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
5278+ const target_endian = elf.endian();
5279+ _, const size = ni.location(&elf.mf).resolve(&elf.mf);
5280+ const node = elf.getNode(ni);
5281+ switch (node) {
5282+ else => |tag| @panic(@tagName(tag)),
5283+ .file, .shdr => {},
5284+ .segment => |phndx| switch (elf.phdrSlice()) {
5285+ inline else => |phdr| {
5286+ const ph = &phdr[phndx];
5287+ ph.filesz = std.mem.nativeTo(@TypeOf(ph.filesz), @intCast(size), target_endian);
5288+ ph.memsz = ph.filesz;
5289+ switch (std.mem.toNative(@TypeOf(ph.type), ph.type, target_endian)) {
5290+ else => unreachable,
5291+ std.elf.PT_NULL => {
5292+ if (size > 0) ph.type = std.mem.nativeTo(
5293+ @TypeOf(ph.type),
5294+ std.elf.PT_LOAD,
5295+ target_endian,
5296+ );
5297+ },
5298+ std.elf.PT_LOAD => {
5299+ if (size == 0) ph.type = std.mem.nativeTo(
5300+ @TypeOf(ph.type),
5301+ std.elf.PT_NULL,
5302+ target_endian,
5303+ );
5304+ },
5305+ std.elf.PT_DYNAMIC, std.elf.PT_INTERP, std.elf.PT_PHDR => {},
5306+ std.elf.PT_TLS => try ni.childrenMoved(elf.base.comp.gpa, &elf.mf),
5307+ }
5308+ },
5309+ },
5310+ .section => |si| switch (elf.shdrSlice()) {
5311+ inline else => |shdr, class| {
5312+ const sym = @field(elf.symPtr(si), @tagName(class));
5313+ const shndx = std.mem.toNative(@TypeOf(sym.shndx), sym.shndx, target_endian);
5314+ const sh = &shdr[shndx];
5315+ switch (std.mem.toNative(@TypeOf(sh.type), sh.type, target_endian)) {
5316+ else => unreachable,
5317+ std.elf.SHT_NULL => {
5318+ if (size > 0) sh.type = std.mem.nativeTo(
5319+ @TypeOf(sh.type),
5320+ std.elf.SHT_PROGBITS,
5321+ target_endian,
5322+ );
5323+ },
5324+ std.elf.SHT_PROGBITS => {
5325+ if (size == 0) sh.type = std.mem.nativeTo(
5326+ @TypeOf(sh.type),
5327+ std.elf.SHT_NULL,
5328+ target_endian,
5329+ );
5330+ },
5331+ std.elf.SHT_SYMTAB => sh.info = std.mem.nativeTo(
5332+ @TypeOf(sh.info),
5333+ @intCast(@divExact(
5334+ size,
5335+ std.mem.toNative(@TypeOf(sh.entsize), sh.entsize, target_endian),
5336+ )),
5337+ target_endian,
5338+ ),
5339+ std.elf.SHT_STRTAB => {},
5340+ }
5341+ sh.size = std.mem.nativeTo(@TypeOf(sh.size), @intCast(size), target_endian);
5342+ },
5343+ },
5344+ .nav, .uav, .lazy_code, .lazy_const_data => {},
5345+ }
5346+}
5347+
5348+pub fn updateExports(
5349+ elf: *Elf,
5350+ pt: Zcu.PerThread,
5351+ exported: Zcu.Exported,
5352+ export_indices: []const Zcu.Export.Index,
5353+) !void {
5354+ return elf.updateExportsInner(pt, exported, export_indices) catch |err| switch (err) {
5355+ error.OutOfMemory => error.OutOfMemory,
5356+ error.LinkFailure => error.AnalysisFail,
5357+ else => |e| switch (elf.base.comp.link_diags.fail(
5358+ "linker failed to update exports: {t}",
5359+ .{e},
5360+ )) {
5361+ error.LinkFailure => return error.AnalysisFail,
5362+ },
5363+ };
5364+}
5365+fn updateExportsInner(
5366+ elf: *Elf,
5367+ pt: Zcu.PerThread,
5368+ exported: Zcu.Exported,
5369+ export_indices: []const Zcu.Export.Index,
5370+) !void {
5371+ const zcu = pt.zcu;
5372+ const gpa = zcu.gpa;
5373+ const ip = &zcu.intern_pool;
5374+
5375+ switch (exported) {
5376+ .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
5377+ .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
5378+ Type.fromInterned(ip.typeOf(uav)).fmt(pt),
5379+ Value.fromInterned(uav).fmtValue(pt),
5380+ }),
5381+ }
5382+ try elf.symtab.ensureUnusedCapacity(gpa, export_indices.len);
5383+ const exported_si: Symbol.Index, const @"type": std.elf.STT = switch (exported) {
5384+ .nav => |nav| .{
5385+ try elf.navSymbol(zcu, nav),
5386+ navType(ip, ip.getNav(nav).status, elf.base.comp.config.any_non_single_threaded),
5387+ },
5388+ .uav => |uav| .{ @enumFromInt(switch (try elf.lowerUav(
5389+ pt,
5390+ uav,
5391+ Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
5392+ export_indices[0].ptr(zcu).src,
5393+ )) {
5394+ .sym_index => |si| si,
5395+ .fail => |em| {
5396+ defer em.destroy(gpa);
5397+ return elf.base.comp.link_diags.fail("{s}", .{em.msg});
5398+ },
5399+ }), .OBJECT },
5400+ };
5401+ while (try elf.idle(pt.tid)) {}
5402+ const exported_ni = exported_si.node(elf);
5403+ const value, const size, const shndx = switch (elf.symPtr(exported_si)) {
5404+ inline else => |exported_sym| .{ exported_sym.value, exported_sym.size, exported_sym.shndx },
5405+ };
5406+ for (export_indices) |export_index| {
5407+ const @"export" = export_index.ptr(zcu);
5408+ const name = @"export".opts.name.toSlice(ip);
5409+ const export_si = try elf.globalSymbol(.{
5410+ .name = name,
5411+ .type = @"type",
5412+ .bind = switch (@"export".opts.linkage) {
5413+ .internal => .LOCAL,
5414+ .strong => .GLOBAL,
5415+ .weak => .WEAK,
5416+ .link_once => return error.LinkOnceUnsupported,
5417+ },
5418+ .visibility = switch (@"export".opts.visibility) {
5419+ .default => .DEFAULT,
5420+ .hidden => .HIDDEN,
5421+ .protected => .PROTECTED,
5422+ },
5423+ });
5424+ export_si.get(elf).ni = exported_ni;
5425+ switch (elf.symPtr(export_si)) {
5426+ inline else => |export_sym| {
5427+ export_sym.value = @intCast(value);
5428+ export_sym.size = @intCast(size);
5429+ export_sym.shndx = shndx;
5430+ },
5431+ }
5432+ export_si.applyTargetRelocs(elf);
5433+ if (std.mem.eql(u8, name, "_start")) {
5434+ elf.entry_hack = exported_si;
5435+ switch (elf.ehdrPtr()) {
5436+ inline else => |ehdr| ehdr.entry = @intCast(value),
5437+ }
5438+ }
5439+ }
5440+}
5441+
5442+pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
5443+ _ = elf;
5444+ _ = exported;
5445+ _ = name;
5446+}
5447+
5448+pub fn dump(elf: *Elf, tid: Zcu.PerThread.Id) void {
5449+ const w = std.debug.lockStderrWriter(&.{});
5450+ defer std.debug.unlockStderrWriter();
5451+ elf.printNode(tid, w, .root, 0) catch {};
5452+}
5453+
5454+pub fn printNode(
5455+ elf: *Elf,
5456+ tid: Zcu.PerThread.Id,
5457+ w: *std.Io.Writer,
5458+ ni: MappedFile.Node.Index,
5459+ indent: usize,
5460+) !void {
5461+ const node = elf.getNode(ni);
5462+ const mf_node = &elf.mf.nodes.items[@intFromEnum(ni)];
5463+ const off, const size = mf_node.location().resolve(&elf.mf);
5464+ try w.splatByteAll(' ', indent);
5465+ try w.writeAll(@tagName(node));
5466+ switch (node) {
5467+ else => {},
5468+ .section => |si| try w.print("({s})", .{elf.sectionName(si)}),
5469+ .nav => |nav_index| {
5470+ const zcu = elf.base.comp.zcu.?;
5471+ const ip = &zcu.intern_pool;
5472+ const nav = ip.getNav(nav_index);
5473+ try w.print("({f}, {f})", .{
5474+ Type.fromInterned(nav.typeOf(ip)).fmt(.{ .zcu = zcu, .tid = tid }),
5475+ nav.fqn.fmt(ip),
5476+ });
5477+ },
5478+ .uav => |uav| {
5479+ const zcu = elf.base.comp.zcu.?;
5480+ const val: Value = .fromInterned(uav);
5481+ try w.print("({f}, {f})", .{
5482+ val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
5483+ val.fmtValue(.{ .zcu = zcu, .tid = tid }),
5484+ });
5485+ },
5486+ }
5487+ try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
5488+ @intFromEnum(ni),
5489+ off,
5490+ size,
5491+ mf_node.flags.alignment.toByteUnits(),
5492+ if (mf_node.flags.fixed) " fixed" else "",
5493+ if (mf_node.flags.moved) " moved" else "",
5494+ if (mf_node.flags.resized) " resized" else "",
5495+ if (mf_node.flags.has_content) " has_content" else "",
5496+ });
5497+ var child_ni = mf_node.first;
5498+ switch (child_ni) {
5499+ .none => {
5500+ const file_loc = ni.fileLocation(&elf.mf, false);
5501+ if (file_loc.size == 0) return;
5502+ var address = file_loc.offset;
5503+ const line_len = 0x10;
5504+ var line_it = std.mem.window(
5505+ u8,
5506+ elf.mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
5507+ line_len,
5508+ line_len,
5509+ );
5510+ while (line_it.next()) |line_bytes| : (address += line_len) {
5511+ try w.splatByteAll(' ', indent + 1);
5512+ try w.print("{x:0>8}", .{address});
5513+ for (line_bytes) |byte| try w.print(" {x:0>2}", .{byte});
5514+ try w.writeByte('\n');
5515+ }
5516+ },
5517+ else => while (child_ni != .none) {
5518+ try elf.printNode(tid, w, child_ni, indent + 1);
5519+ child_ni = elf.mf.nodes.items[@intFromEnum(child_ni)].next;
5520+ },
5521+ }
5522+}
5523+
5524+const assert = std.debug.assert;
5525+const builtin = @import("builtin");
5526+const codegen = @import("../codegen.zig");
5527+const Compilation = @import("../Compilation.zig");
5528+const Elf = @This();
5529+const InternPool = @import("../InternPool.zig");
5530+const link = @import("../link.zig");
5531+const log = std.log.scoped(.link);
5532+const MappedFile = @import("MappedFile.zig");
5533+const native_endian = builtin.cpu.arch.endian();
5534+const std = @import("std");
5535+const target_util = @import("../target.zig");
5536+const Type = @import("../Type.zig");
5537+const Value = @import("../Value.zig");
5538+const Zcu = @import("../Zcu.zig");
5539diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig
5540index b1fc6528d5808663942b0428869f0c72efda1931..5a0a71f380c8a83c66b4f4e5391e4ec54469033e 100644
5541--- a/src/link/MachO/ZigObject.zig
5542+++ b/src/link/MachO/ZigObject.zig
5543@@ -784,22 +784,26 @@ pub fn updateFunc(
5544 const sym_index = try self.getOrCreateMetadataForNav(macho_file, func.owner_nav);
5545 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
5546
5547- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
5548- defer code_buffer.deinit(gpa);
5549+ var aw: std.Io.Writer.Allocating = .init(gpa);
5550+ defer aw.deinit();
5551
5552 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, func.owner_nav, sym_index) else null;
5553 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
5554
5555- try codegen.emitFunction(
5556+ codegen.emitFunction(
5557 &macho_file.base,
5558 pt,
5559 zcu.navSrcLoc(func.owner_nav),
5560 func_index,
5561+ sym_index,
5562 mir,
5563- &code_buffer,
5564+ &aw.writer,
5565 if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
5566- );
5567- const code = code_buffer.items;
5568+ ) catch |err| switch (err) {
5569+ error.WriteFailed => return error.OutOfMemory,
5570+ else => |e| return e,
5571+ };
5572+ const code = aw.written();
5573
5574 const sect_index = try self.getNavOutputSection(macho_file, zcu, func.owner_nav, code);
5575 const old_rva, const old_alignment = blk: {
5576@@ -895,21 +899,24 @@ pub fn updateNav(
5577 const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index);
5578 self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file);
5579
5580- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
5581- defer code_buffer.deinit(zcu.gpa);
5582+ var aw: std.Io.Writer.Allocating = .init(zcu.gpa);
5583+ defer aw.deinit();
5584
5585 var debug_wip_nav = if (self.dwarf) |*dwarf| try dwarf.initWipNav(pt, nav_index, sym_index) else null;
5586 defer if (debug_wip_nav) |*wip_nav| wip_nav.deinit();
5587
5588- try codegen.generateSymbol(
5589+ codegen.generateSymbol(
5590 &macho_file.base,
5591 pt,
5592 zcu.navSrcLoc(nav_index),
5593 Value.fromInterned(nav_init),
5594- &code_buffer,
5595+ &aw.writer,
5596 .{ .atom_index = sym_index },
5597- );
5598- const code = code_buffer.items;
5599+ ) catch |err| switch (err) {
5600+ error.WriteFailed => return error.OutOfMemory,
5601+ else => |e| return e,
5602+ };
5603+ const code = aw.written();
5604
5605 const sect_index = try self.getNavOutputSection(macho_file, zcu, nav_index, code);
5606 if (isThreadlocal(macho_file, nav_index))
5607@@ -1198,21 +1205,24 @@ fn lowerConst(
5608 ) !codegen.SymbolResult {
5609 const gpa = macho_file.base.comp.gpa;
5610
5611- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
5612- defer code_buffer.deinit(gpa);
5613+ var aw: std.Io.Writer.Allocating = .init(gpa);
5614+ defer aw.deinit();
5615
5616 const name_str = try self.addString(gpa, name);
5617 const sym_index = try self.newSymbolWithAtom(gpa, name_str, macho_file);
5618
5619- try codegen.generateSymbol(
5620+ codegen.generateSymbol(
5621 &macho_file.base,
5622 pt,
5623 src_loc,
5624 val,
5625- &code_buffer,
5626+ &aw.writer,
5627 .{ .atom_index = sym_index },
5628- );
5629- const code = code_buffer.items;
5630+ ) catch |err| switch (err) {
5631+ error.WriteFailed => return error.OutOfMemory,
5632+ else => |e| return e,
5633+ };
5634+ const code = aw.written();
5635
5636 const sym = &self.symbols.items[sym_index];
5637 sym.out_n_sect = output_section_index;
5638@@ -1349,8 +1359,8 @@ fn updateLazySymbol(
5639 const gpa = zcu.gpa;
5640
5641 var required_alignment: Atom.Alignment = .none;
5642- var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
5643- defer code_buffer.deinit(gpa);
5644+ var aw: std.Io.Writer.Allocating = .init(gpa);
5645+ defer aw.deinit();
5646
5647 const name_str = blk: {
5648 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
5649@@ -1368,11 +1378,11 @@ fn updateLazySymbol(
5650 src,
5651 lazy_sym,
5652 &required_alignment,
5653- &code_buffer,
5654+ &aw.writer,
5655 .none,
5656 .{ .atom_index = symbol_index },
5657 );
5658- const code = code_buffer.items;
5659+ const code = aw.written();
5660
5661 const output_section_index = switch (lazy_sym.kind) {
5662 .code => macho_file.zig_text_sect_index.?,
5663diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig
5664new file mode 100644
5665index 0000000000000000000000000000000000000000..d04a8d533a03f8934a8546311007b867bde353d0
5666--- /dev/null
5667+++ b/src/link/MappedFile.zig
5668@@ -0,0 +1,929 @@
5669+file: std.fs.File,
5670+flags: packed struct {
5671+ block_size: std.mem.Alignment,
5672+ copy_file_range_unsupported: bool,
5673+ fallocate_punch_hole_unsupported: bool,
5674+ fallocate_insert_range_unsupported: bool,
5675+},
5676+section: if (is_windows) windows.HANDLE else void,
5677+contents: []align(std.heap.page_size_min) u8,
5678+nodes: std.ArrayList(Node),
5679+free_ni: Node.Index,
5680+large: std.ArrayList(u64),
5681+updates: std.ArrayList(Node.Index),
5682+update_prog_node: std.Progress.Node,
5683+writers: std.SinglyLinkedList,
5684+
5685+pub const Error = std.posix.MMapError ||
5686+ std.posix.MRemapError ||
5687+ std.fs.File.SetEndPosError ||
5688+ std.fs.File.CopyRangeError ||
5689+ error{NotFile};
5690+
5691+pub fn init(file: std.fs.File, gpa: std.mem.Allocator) !MappedFile {
5692+ var mf: MappedFile = .{
5693+ .file = file,
5694+ .flags = undefined,
5695+ .section = if (is_windows) windows.INVALID_HANDLE_VALUE else {},
5696+ .contents = &.{},
5697+ .nodes = .empty,
5698+ .free_ni = .none,
5699+ .large = .empty,
5700+ .updates = .empty,
5701+ .update_prog_node = .none,
5702+ .writers = .{},
5703+ };
5704+ errdefer mf.deinit(gpa);
5705+ const size: u64, const blksize = if (is_windows)
5706+ .{ try windows.GetFileSizeEx(file.handle), 1 }
5707+ else stat: {
5708+ const stat = try std.posix.fstat(mf.file.handle);
5709+ if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists;
5710+ break :stat .{ @bitCast(stat.size), stat.blksize };
5711+ };
5712+ mf.flags = .{
5713+ .block_size = .fromByteUnits(
5714+ std.math.ceilPowerOfTwoAssert(usize, @max(std.heap.pageSize(), blksize)),
5715+ ),
5716+ .copy_file_range_unsupported = false,
5717+ .fallocate_insert_range_unsupported = false,
5718+ .fallocate_punch_hole_unsupported = false,
5719+ };
5720+ try mf.nodes.ensureUnusedCapacity(gpa, 1);
5721+ assert(try mf.addNode(gpa, .{
5722+ .add_node = .{
5723+ .size = size,
5724+ .fixed = true,
5725+ },
5726+ }) == Node.Index.root);
5727+ try mf.ensureTotalCapacity(@intCast(size));
5728+ return mf;
5729+}
5730+
5731+pub fn deinit(mf: *MappedFile, gpa: std.mem.Allocator) void {
5732+ mf.unmap();
5733+ mf.nodes.deinit(gpa);
5734+ mf.large.deinit(gpa);
5735+ mf.updates.deinit(gpa);
5736+ mf.update_prog_node.end();
5737+ assert(mf.writers.first == null);
5738+ mf.* = undefined;
5739+}
5740+
5741+pub const Node = extern struct {
5742+ parent: Node.Index,
5743+ prev: Node.Index,
5744+ next: Node.Index,
5745+ first: Node.Index,
5746+ last: Node.Index,
5747+ flags: Flags,
5748+ location_payload: Location.Payload,
5749+
5750+ pub const Flags = packed struct(u32) {
5751+ location_tag: Location.Tag,
5752+ alignment: std.mem.Alignment,
5753+ /// Whether this node can be moved.
5754+ fixed: bool,
5755+ /// Whether this node has been moved.
5756+ moved: bool,
5757+ /// Whether this node has been resized.
5758+ resized: bool,
5759+ /// Whether this node might contain non-zero bytes.
5760+ has_content: bool,
5761+ unused: @Type(.{ .int = .{
5762+ .signedness = .unsigned,
5763+ .bits = 32 - @bitSizeOf(std.mem.Alignment) - 5,
5764+ } }) = 0,
5765+ };
5766+
5767+ pub const Location = union(enum(u1)) {
5768+ small: extern struct {
5769+ /// Relative to `parent`.
5770+ offset: u32,
5771+ size: u32,
5772+ },
5773+ large: extern struct {
5774+ index: usize,
5775+ unused: @Type(.{ .int = .{
5776+ .signedness = .unsigned,
5777+ .bits = 64 - @bitSizeOf(usize),
5778+ } }) = 0,
5779+ },
5780+
5781+ pub const Tag = @typeInfo(Location).@"union".tag_type.?;
5782+ pub const Payload = @Type(.{ .@"union" = .{
5783+ .layout = .@"extern",
5784+ .tag_type = null,
5785+ .fields = @typeInfo(Location).@"union".fields,
5786+ .decls = &.{},
5787+ } });
5788+
5789+ pub fn resolve(loc: Location, mf: *const MappedFile) [2]u64 {
5790+ return switch (loc) {
5791+ .small => |small| .{ small.offset, small.size },
5792+ .large => |large| mf.large.items[large.index..][0..2].*,
5793+ };
5794+ }
5795+ };
5796+
5797+ pub const Index = enum(u32) {
5798+ none,
5799+ _,
5800+
5801+ pub const root: Node.Index = .none;
5802+
5803+ fn get(ni: Node.Index, mf: *const MappedFile) *Node {
5804+ return &mf.nodes.items[@intFromEnum(ni)];
5805+ }
5806+
5807+ pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
5808+ var child_ni = ni.get(mf).last;
5809+ while (child_ni != .none) {
5810+ try child_ni.moved(gpa, mf);
5811+ child_ni = child_ni.get(mf).prev;
5812+ }
5813+ }
5814+
5815+ pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
5816+ var parent_ni = ni;
5817+ while (parent_ni != .none) {
5818+ const parent = parent_ni.get(mf);
5819+ if (parent.flags.moved) return true;
5820+ parent_ni = parent.parent;
5821+ }
5822+ return false;
5823+ }
5824+ pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
5825+ try mf.updates.ensureUnusedCapacity(gpa, 1);
5826+ ni.movedAssumeCapacity(mf);
5827+ }
5828+ pub fn cleanMoved(ni: Node.Index, mf: *const MappedFile) bool {
5829+ const node_moved = &ni.get(mf).flags.moved;
5830+ defer node_moved.* = false;
5831+ return node_moved.*;
5832+ }
5833+ fn movedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void {
5834+ var parent_ni = ni;
5835+ while (parent_ni != .none) {
5836+ const parent_node = parent_ni.get(mf);
5837+ if (parent_node.flags.moved) return;
5838+ parent_ni = parent_node.parent;
5839+ }
5840+ const node = ni.get(mf);
5841+ node.flags.moved = true;
5842+ if (node.flags.resized) return;
5843+ mf.updates.appendAssumeCapacity(ni);
5844+ mf.update_prog_node.increaseEstimatedTotalItems(1);
5845+ }
5846+
5847+ pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
5848+ return ni.get(mf).flags.resized;
5849+ }
5850+ pub fn resized(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) !void {
5851+ try mf.updates.ensureUnusedCapacity(gpa, 1);
5852+ ni.resizedAssumeCapacity(mf);
5853+ }
5854+ pub fn cleanResized(ni: Node.Index, mf: *const MappedFile) bool {
5855+ const node_resized = &ni.get(mf).flags.resized;
5856+ defer node_resized.* = false;
5857+ return node_resized.*;
5858+ }
5859+ fn resizedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void {
5860+ const node = ni.get(mf);
5861+ if (node.flags.resized) return;
5862+ node.flags.resized = true;
5863+ if (node.flags.moved) return;
5864+ mf.updates.appendAssumeCapacity(ni);
5865+ mf.update_prog_node.increaseEstimatedTotalItems(1);
5866+ }
5867+
5868+ pub fn alignment(ni: Node.Index, mf: *const MappedFile) std.mem.Alignment {
5869+ return ni.get(mf).flags.alignment;
5870+ }
5871+
5872+ fn setLocationAssumeCapacity(ni: Node.Index, mf: *MappedFile, offset: u64, size: u64) void {
5873+ const node = ni.get(mf);
5874+ if (size == 0) node.flags.has_content = false;
5875+ switch (node.location()) {
5876+ .small => |small| {
5877+ if (small.offset != offset) ni.movedAssumeCapacity(mf);
5878+ if (small.size != size) ni.resizedAssumeCapacity(mf);
5879+ if (std.math.cast(u32, offset)) |small_offset| {
5880+ if (std.math.cast(u32, size)) |small_size| {
5881+ node.location_payload.small = .{
5882+ .offset = small_offset,
5883+ .size = small_size,
5884+ };
5885+ return;
5886+ }
5887+ }
5888+ defer mf.large.appendSliceAssumeCapacity(&.{ offset, size });
5889+ node.flags.location_tag = .large;
5890+ node.location_payload = .{ .large = .{ .index = mf.large.items.len } };
5891+ },
5892+ .large => |large| {
5893+ const large_items = mf.large.items[large.index..][0..2];
5894+ if (large_items[0] != offset) ni.movedAssumeCapacity(mf);
5895+ if (large_items[1] != size) ni.resizedAssumeCapacity(mf);
5896+ large_items.* = .{ offset, size };
5897+ },
5898+ }
5899+ }
5900+
5901+ pub fn location(ni: Node.Index, mf: *const MappedFile) Location {
5902+ return ni.get(mf).location();
5903+ }
5904+
5905+ pub fn fileLocation(
5906+ ni: Node.Index,
5907+ mf: *const MappedFile,
5908+ set_has_content: bool,
5909+ ) struct { offset: u64, size: u64 } {
5910+ var offset, const size = ni.location(mf).resolve(mf);
5911+ var parent_ni = ni;
5912+ while (true) {
5913+ const parent = parent_ni.get(mf);
5914+ if (set_has_content) parent.flags.has_content = true;
5915+ if (parent_ni == .none) break;
5916+ parent_ni = parent.parent;
5917+ offset += parent_ni.location(mf).resolve(mf)[0];
5918+ }
5919+ return .{ .offset = offset, .size = size };
5920+ }
5921+
5922+ pub fn slice(ni: Node.Index, mf: *const MappedFile) []u8 {
5923+ const file_loc = ni.fileLocation(mf, true);
5924+ return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
5925+ }
5926+
5927+ pub fn sliceConst(ni: Node.Index, mf: *const MappedFile) []const u8 {
5928+ const file_loc = ni.fileLocation(mf, false);
5929+ return mf.contents[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
5930+ }
5931+
5932+ pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) !void {
5933+ try mf.resizeNode(gpa, ni, size);
5934+ var writers_it = mf.writers.first;
5935+ while (writers_it) |writer_node| : (writers_it = writer_node.next) {
5936+ const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node);
5937+ w.interface.buffer = w.ni.slice(mf);
5938+ }
5939+ }
5940+
5941+ pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void {
5942+ w.* = .{
5943+ .gpa = gpa,
5944+ .mf = mf,
5945+ .writer_node = .{},
5946+ .ni = ni,
5947+ .interface = .{
5948+ .buffer = ni.slice(mf),
5949+ .vtable = &Writer.vtable,
5950+ },
5951+ .err = null,
5952+ };
5953+ mf.writers.prepend(&w.writer_node);
5954+ }
5955+ };
5956+
5957+ pub fn location(node: *const Node) Location {
5958+ return switch (node.flags.location_tag) {
5959+ inline else => |tag| @unionInit(
5960+ Location,
5961+ @tagName(tag),
5962+ @field(node.location_payload, @tagName(tag)),
5963+ ),
5964+ };
5965+ }
5966+
5967+ pub const Writer = struct {
5968+ gpa: std.mem.Allocator,
5969+ mf: *MappedFile,
5970+ writer_node: std.SinglyLinkedList.Node,
5971+ ni: Node.Index,
5972+ interface: std.Io.Writer,
5973+ err: ?Error,
5974+
5975+ pub fn deinit(w: *Writer) void {
5976+ assert(w.mf.writers.popFirst() == &w.writer_node);
5977+ w.* = undefined;
5978+ }
5979+
5980+ const vtable: std.Io.Writer.VTable = .{
5981+ .drain = drain,
5982+ .sendFile = sendFile,
5983+ .flush = std.Io.Writer.noopFlush,
5984+ .rebase = growingRebase,
5985+ };
5986+
5987+ fn drain(
5988+ interface: *std.Io.Writer,
5989+ data: []const []const u8,
5990+ splat: usize,
5991+ ) std.Io.Writer.Error!usize {
5992+ const pattern = data[data.len - 1];
5993+ const splat_len = pattern.len * splat;
5994+ const start_len = interface.end;
5995+ assert(data.len != 0);
5996+ for (data) |bytes| {
5997+ try growingRebase(interface, interface.end, bytes.len + splat_len + 1);
5998+ @memcpy(interface.buffer[interface.end..][0..bytes.len], bytes);
5999+ interface.end += bytes.len;
6000+ }
6001+ if (splat == 0) {
6002+ interface.end -= pattern.len;
6003+ } else switch (pattern.len) {
6004+ 0 => {},
6005+ 1 => {
6006+ @memset(interface.buffer[interface.end..][0 .. splat - 1], pattern[0]);
6007+ interface.end += splat - 1;
6008+ },
6009+ else => for (0..splat - 1) |_| {
6010+ @memcpy(interface.buffer[interface.end..][0..pattern.len], pattern);
6011+ interface.end += pattern.len;
6012+ },
6013+ }
6014+ return interface.end - start_len;
6015+ }
6016+
6017+ fn sendFile(
6018+ interface: *std.Io.Writer,
6019+ file_reader: *std.fs.File.Reader,
6020+ limit: std.Io.Limit,
6021+ ) std.Io.Writer.FileError!usize {
6022+ if (limit == .nothing) return 0;
6023+ const pos = file_reader.logicalPos();
6024+ const additional = if (file_reader.getSize()) |size| size - pos else |_| std.atomic.cache_line;
6025+ if (additional == 0) return error.EndOfStream;
6026+ try growingRebase(interface, interface.end, limit.minInt64(additional));
6027+ switch (file_reader.mode) {
6028+ .positional => {
6029+ const fr_buf = file_reader.interface.buffered();
6030+ const buf_copy_size = interface.write(fr_buf) catch unreachable;
6031+ file_reader.interface.toss(buf_copy_size);
6032+ if (buf_copy_size < fr_buf.len) return buf_copy_size;
6033+ assert(file_reader.logicalPos() == file_reader.pos);
6034+
6035+ const w: *Writer = @fieldParentPtr("interface", interface);
6036+ const copy_size: usize = @intCast(w.mf.copyFileRange(
6037+ file_reader.file,
6038+ file_reader.pos,
6039+ w.ni.fileLocation(w.mf, true).offset + interface.end,
6040+ limit.minInt(interface.unusedCapacityLen()),
6041+ ) catch |err| {
6042+ w.err = err;
6043+ return error.WriteFailed;
6044+ });
6045+ interface.end += copy_size;
6046+ return copy_size;
6047+ },
6048+ .streaming,
6049+ .streaming_reading,
6050+ .positional_reading,
6051+ .failure,
6052+ => {
6053+ const dest = limit.slice(interface.unusedCapacitySlice());
6054+ const n = try file_reader.read(dest);
6055+ interface.end += n;
6056+ return n;
6057+ },
6058+ }
6059+ }
6060+
6061+ fn growingRebase(
6062+ interface: *std.Io.Writer,
6063+ preserve: usize,
6064+ unused_capacity: usize,
6065+ ) std.Io.Writer.Error!void {
6066+ _ = preserve;
6067+ const total_capacity = interface.end + unused_capacity;
6068+ if (interface.buffer.len >= total_capacity) return;
6069+ const w: *Writer = @fieldParentPtr("interface", interface);
6070+ w.ni.resize(w.mf, w.gpa, total_capacity +| total_capacity / 2) catch |err| {
6071+ w.err = err;
6072+ return error.WriteFailed;
6073+ };
6074+ }
6075+ };
6076+
6077+ comptime {
6078+ if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 32);
6079+ }
6080+};
6081+
6082+fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
6083+ parent: Node.Index = .none,
6084+ prev: Node.Index = .none,
6085+ next: Node.Index = .none,
6086+ offset: u64 = 0,
6087+ add_node: AddNodeOptions,
6088+}) !Node.Index {
6089+ if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
6090+ const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
6091+ const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
6092+ if (std.math.cast(u32, offset)) |small_offset| break :location .{ .small, .{
6093+ .small = .{ .offset = small_offset, .size = 0 },
6094+ } };
6095+ try mf.large.ensureUnusedCapacity(gpa, 2);
6096+ defer mf.large.appendSliceAssumeCapacity(&.{ offset, 0 });
6097+ break :location .{ .large, .{ .large = .{ .index = mf.large.items.len } } };
6098+ };
6099+ const free_ni: Node.Index, const free_node = free: switch (mf.free_ni) {
6100+ .none => .{ @enumFromInt(mf.nodes.items.len), mf.nodes.addOneAssumeCapacity() },
6101+ else => |free_ni| {
6102+ const free_node = free_ni.get(mf);
6103+ mf.free_ni = free_node.next;
6104+ break :free .{ free_ni, free_node };
6105+ },
6106+ };
6107+ free_node.* = .{
6108+ .parent = opts.parent,
6109+ .prev = opts.prev,
6110+ .next = opts.next,
6111+ .first = .none,
6112+ .last = .none,
6113+ .flags = .{
6114+ .location_tag = location_tag,
6115+ .alignment = opts.add_node.alignment,
6116+ .fixed = opts.add_node.fixed,
6117+ .moved = true,
6118+ .resized = true,
6119+ .has_content = false,
6120+ },
6121+ .location_payload = location_payload,
6122+ };
6123+ {
6124+ defer {
6125+ free_node.flags.moved = false;
6126+ free_node.flags.resized = false;
6127+ }
6128+ if (offset > opts.parent.location(mf).resolve(mf)[1]) try opts.parent.resize(mf, gpa, offset);
6129+ try free_ni.resize(mf, gpa, opts.add_node.size);
6130+ }
6131+ if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
6132+ if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
6133+ return free_ni;
6134+}
6135+
6136+pub const AddNodeOptions = struct {
6137+ size: u64 = 0,
6138+ alignment: std.mem.Alignment = .@"1",
6139+ fixed: bool = false,
6140+ moved: bool = false,
6141+ resized: bool = false,
6142+};
6143+
6144+pub fn addOnlyChildNode(
6145+ mf: *MappedFile,
6146+ gpa: std.mem.Allocator,
6147+ parent_ni: Node.Index,
6148+ opts: AddNodeOptions,
6149+) !Node.Index {
6150+ try mf.nodes.ensureUnusedCapacity(gpa, 1);
6151+ const parent = parent_ni.get(mf);
6152+ assert(parent.first == .none and parent.last == .none);
6153+ const ni = try mf.addNode(gpa, .{
6154+ .parent = parent_ni,
6155+ .add_node = opts,
6156+ });
6157+ parent.first = ni;
6158+ parent.last = ni;
6159+ return ni;
6160+}
6161+
6162+pub fn addLastChildNode(
6163+ mf: *MappedFile,
6164+ gpa: std.mem.Allocator,
6165+ parent_ni: Node.Index,
6166+ opts: AddNodeOptions,
6167+) !Node.Index {
6168+ try mf.nodes.ensureUnusedCapacity(gpa, 1);
6169+ const parent = parent_ni.get(mf);
6170+ const ni = try mf.addNode(gpa, .{
6171+ .parent = parent_ni,
6172+ .prev = parent.last,
6173+ .offset = offset: switch (parent.last) {
6174+ .none => 0,
6175+ else => |last_ni| {
6176+ const last_offset, const last_size = last_ni.location(mf).resolve(mf);
6177+ break :offset last_offset + last_size;
6178+ },
6179+ },
6180+ .add_node = opts,
6181+ });
6182+ switch (parent.last) {
6183+ .none => parent.first = ni,
6184+ else => |last_ni| last_ni.get(mf).next = ni,
6185+ }
6186+ parent.last = ni;
6187+ return ni;
6188+}
6189+
6190+pub fn addNodeAfter(
6191+ mf: *MappedFile,
6192+ gpa: std.mem.Allocator,
6193+ prev_ni: Node.Index,
6194+ opts: AddNodeOptions,
6195+) !Node.Index {
6196+ assert(prev_ni != .none);
6197+ try mf.nodes.ensureUnusedCapacity(gpa, 1);
6198+ const prev = prev_ni.get(mf);
6199+ const prev_offset, const prev_size = prev.location().resolve(mf);
6200+ const ni = try mf.addNode(gpa, .{
6201+ .parent = prev.parent,
6202+ .prev = prev_ni,
6203+ .next = prev.next,
6204+ .offset = prev_offset + prev_size,
6205+ .add_node = opts,
6206+ });
6207+ switch (prev.next) {
6208+ .none => prev.parent.get(mf).last = ni,
6209+ else => |next_ni| next_ni.get(mf).prev = ni,
6210+ }
6211+ prev.next = ni;
6212+ return ni;
6213+}
6214+
6215+fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) !void {
6216+ const node = ni.get(mf);
6217+ var old_offset, const old_size = node.location().resolve(mf);
6218+ const new_size = node.flags.alignment.forward(@intCast(requested_size));
6219+ // Resize the entire file
6220+ if (ni == Node.Index.root) {
6221+ try mf.file.setEndPos(new_size);
6222+ try mf.ensureTotalCapacity(@intCast(new_size));
6223+ try mf.ensureCapacityForSetLocation(gpa);
6224+ ni.setLocationAssumeCapacity(mf, old_offset, new_size);
6225+ return;
6226+ }
6227+ while (true) {
6228+ const parent = node.parent.get(mf);
6229+ _, const old_parent_size = parent.location().resolve(mf);
6230+ const trailing_end = switch (node.next) {
6231+ .none => parent.location().resolve(mf)[1],
6232+ else => |next_ni| next_ni.location(mf).resolve(mf)[0],
6233+ };
6234+ assert(old_offset + old_size <= trailing_end);
6235+ // Expand the node into available trailing free space
6236+ if (old_offset + new_size <= trailing_end) {
6237+ try mf.ensureCapacityForSetLocation(gpa);
6238+ ni.setLocationAssumeCapacity(mf, old_offset, new_size);
6239+ return;
6240+ }
6241+ // Ask the filesystem driver to insert an extent into the file without copying any data
6242+ if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
6243+ node.flags.alignment.order(mf.flags.block_size).compare(.gte))
6244+ insert_range: {
6245+ const last_offset, const last_size = parent.last.location(mf).resolve(mf);
6246+ const last_end = last_offset + last_size;
6247+ assert(last_end <= old_parent_size);
6248+ const range_size =
6249+ node.flags.alignment.forward(@intCast(requested_size +| requested_size / 2)) - old_size;
6250+ const new_parent_size = last_end + range_size;
6251+ if (new_parent_size > old_parent_size) {
6252+ try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2);
6253+ continue;
6254+ }
6255+ const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
6256+ retry: while (true) {
6257+ switch (linux.E.init(linux.fallocate(
6258+ mf.file.handle,
6259+ linux.FALLOC.FL_INSERT_RANGE,
6260+ @intCast(range_file_offset),
6261+ @intCast(range_size),
6262+ ))) {
6263+ .SUCCESS => {
6264+ var enclosing_ni = ni;
6265+ while (enclosing_ni != .none) {
6266+ try mf.ensureCapacityForSetLocation(gpa);
6267+ const enclosing = enclosing_ni.get(mf);
6268+ const enclosing_offset, const enclosing_size =
6269+ enclosing.location().resolve(mf);
6270+ enclosing_ni.setLocationAssumeCapacity(
6271+ mf,
6272+ enclosing_offset,
6273+ enclosing_size + range_size,
6274+ );
6275+ var after_ni = enclosing.next;
6276+ while (after_ni != .none) {
6277+ try mf.ensureCapacityForSetLocation(gpa);
6278+ const after = after_ni.get(mf);
6279+ const after_offset, const after_size = after.location().resolve(mf);
6280+ after_ni.setLocationAssumeCapacity(
6281+ mf,
6282+ range_size + after_offset,
6283+ after_size,
6284+ );
6285+ after_ni = after.next;
6286+ }
6287+ enclosing_ni = enclosing.parent;
6288+ }
6289+ return;
6290+ },
6291+ .INTR => continue :retry,
6292+ .BADF, .FBIG, .INVAL => unreachable,
6293+ .IO => return error.InputOutput,
6294+ .NODEV => return error.NotFile,
6295+ .NOSPC => return error.NoSpaceLeft,
6296+ .NOSYS, .OPNOTSUPP => {
6297+ mf.flags.fallocate_insert_range_unsupported = true;
6298+ break :insert_range;
6299+ },
6300+ .PERM => return error.PermissionDenied,
6301+ .SPIPE => return error.Unseekable,
6302+ .TXTBSY => return error.FileBusy,
6303+ else => |e| return std.posix.unexpectedErrno(e),
6304+ }
6305+ }
6306+ }
6307+ switch (node.next) {
6308+ .none => {
6309+ // As this is the last node, we simply need more space in the parent
6310+ const new_parent_size = old_offset + new_size;
6311+ try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / 2);
6312+ },
6313+ else => |*next_ni_ptr| switch (node.flags.fixed) {
6314+ false => {
6315+ // Make space at the end of the parent for this floating node
6316+ const last = parent.last.get(mf);
6317+ const last_offset, const last_size = last.location().resolve(mf);
6318+ const new_offset = node.flags.alignment.forward(@intCast(last_offset + last_size));
6319+ const new_parent_size = new_offset + new_size;
6320+ if (new_parent_size > old_parent_size) {
6321+ try mf.resizeNode(
6322+ gpa,
6323+ node.parent,
6324+ new_parent_size +| new_parent_size / 2,
6325+ );
6326+ continue;
6327+ }
6328+ const next_ni = next_ni_ptr.*;
6329+ next_ni.get(mf).prev = node.prev;
6330+ switch (node.prev) {
6331+ .none => parent.first = next_ni,
6332+ else => |prev_ni| prev_ni.get(mf).next = next_ni,
6333+ }
6334+ last.next = ni;
6335+ node.prev = parent.last;
6336+ next_ni_ptr.* = .none;
6337+ parent.last = ni;
6338+ if (node.flags.has_content) {
6339+ const parent_file_offset = node.parent.fileLocation(mf, false).offset;
6340+ try mf.moveRange(
6341+ parent_file_offset + old_offset,
6342+ parent_file_offset + new_offset,
6343+ old_size,
6344+ );
6345+ }
6346+ old_offset = new_offset;
6347+ },
6348+ true => {
6349+ // Move the next floating node to make space for this fixed node
6350+ const next_ni = next_ni_ptr.*;
6351+ const next = next_ni.get(mf);
6352+ assert(!next.flags.fixed);
6353+ const next_offset, const next_size = next.location().resolve(mf);
6354+ const last = parent.last.get(mf);
6355+ const last_offset, const last_size = last.location().resolve(mf);
6356+ const new_offset = next.flags.alignment.forward(@intCast(
6357+ @max(old_offset + new_size, last_offset + last_size),
6358+ ));
6359+ const new_parent_size = new_offset + next_size;
6360+ if (new_parent_size > old_parent_size) {
6361+ try mf.resizeNode(
6362+ gpa,
6363+ node.parent,
6364+ new_parent_size +| new_parent_size / 2,
6365+ );
6366+ continue;
6367+ }
6368+ try mf.ensureCapacityForSetLocation(gpa);
6369+ next.prev = parent.last;
6370+ parent.last = next_ni;
6371+ last.next = next_ni;
6372+ next_ni_ptr.* = next.next;
6373+ switch (next.next) {
6374+ .none => {},
6375+ else => |next_next_ni| next_next_ni.get(mf).prev = ni,
6376+ }
6377+ next.next = .none;
6378+ if (node.flags.has_content) {
6379+ const parent_file_offset = node.parent.fileLocation(mf, false).offset;
6380+ try mf.moveRange(
6381+ parent_file_offset + next_offset,
6382+ parent_file_offset + new_offset,
6383+ next_size,
6384+ );
6385+ }
6386+ next_ni.setLocationAssumeCapacity(mf, new_offset, next_size);
6387+ },
6388+ },
6389+ }
6390+ }
6391+}
6392+
6393+fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
6394+ // make a copy of this node at the new location
6395+ try mf.copyRange(old_file_offset, new_file_offset, size);
6396+ // delete the copy of this node at the old location
6397+ if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and
6398+ size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)
6399+ {
6400+ switch (linux.E.init(linux.fallocate(
6401+ mf.file.handle,
6402+ linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
6403+ @intCast(old_file_offset),
6404+ @intCast(size),
6405+ ))) {
6406+ .SUCCESS => return,
6407+ .INTR => continue,
6408+ .BADF, .FBIG, .INVAL => unreachable,
6409+ .IO => return error.InputOutput,
6410+ .NODEV => return error.NotFile,
6411+ .NOSPC => return error.NoSpaceLeft,
6412+ .NOSYS, .OPNOTSUPP => {
6413+ mf.flags.fallocate_punch_hole_unsupported = true;
6414+ break;
6415+ },
6416+ .PERM => return error.PermissionDenied,
6417+ .SPIPE => return error.Unseekable,
6418+ .TXTBSY => return error.FileBusy,
6419+ else => |e| return std.posix.unexpectedErrno(e),
6420+ }
6421+ };
6422+ @memset(mf.contents[@intCast(old_file_offset)..][0..@intCast(size)], 0);
6423+}
6424+
6425+fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
6426+ const copy_size = try mf.copyFileRange(mf.file, old_file_offset, new_file_offset, size);
6427+ if (copy_size < size) @memcpy(
6428+ mf.contents[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
6429+ mf.contents[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
6430+ );
6431+}
6432+
6433+fn copyFileRange(
6434+ mf: *MappedFile,
6435+ old_file: std.fs.File,
6436+ old_file_offset: u64,
6437+ new_file_offset: u64,
6438+ size: u64,
6439+) !u64 {
6440+ var remaining_size = size;
6441+ if (is_linux and !mf.flags.copy_file_range_unsupported) {
6442+ var old_file_offset_mut: i64 = @intCast(old_file_offset);
6443+ var new_file_offset_mut: i64 = @intCast(new_file_offset);
6444+ while (remaining_size >= mf.flags.block_size.toByteUnits() * 2 - 1) {
6445+ const copy_len = linux.copy_file_range(
6446+ old_file.handle,
6447+ &old_file_offset_mut,
6448+ mf.file.handle,
6449+ &new_file_offset_mut,
6450+ @intCast(remaining_size),
6451+ 0,
6452+ );
6453+ switch (linux.E.init(copy_len)) {
6454+ .SUCCESS => {
6455+ if (copy_len == 0) break;
6456+ remaining_size -= copy_len;
6457+ if (remaining_size == 0) break;
6458+ },
6459+ .INTR => continue,
6460+ .BADF, .FBIG, .INVAL, .OVERFLOW => unreachable,
6461+ .IO => return error.InputOutput,
6462+ .ISDIR => return error.IsDir,
6463+ .NOMEM => return error.SystemResources,
6464+ .NOSPC => return error.NoSpaceLeft,
6465+ .NOSYS, .OPNOTSUPP, .XDEV => {
6466+ mf.flags.copy_file_range_unsupported = true;
6467+ break;
6468+ },
6469+ .PERM => return error.PermissionDenied,
6470+ .TXTBSY => return error.FileBusy,
6471+ else => |e| return std.posix.unexpectedErrno(e),
6472+ }
6473+ }
6474+ }
6475+ return size - remaining_size;
6476+}
6477+
6478+fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) !void {
6479+ try mf.large.ensureUnusedCapacity(gpa, 2);
6480+ try mf.updates.ensureUnusedCapacity(gpa, 1);
6481+}
6482+
6483+pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) !void {
6484+ if (mf.contents.len >= new_capacity) return;
6485+ try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / 2);
6486+}
6487+
6488+pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) !void {
6489+ if (mf.contents.len >= new_capacity) return;
6490+ const aligned_capacity = mf.flags.block_size.forward(new_capacity);
6491+ if (!is_linux) mf.unmap() else if (mf.contents.len > 0) {
6492+ mf.contents = try std.posix.mremap(
6493+ mf.contents.ptr,
6494+ mf.contents.len,
6495+ aligned_capacity,
6496+ .{ .MAYMOVE = true },
6497+ null,
6498+ );
6499+ return;
6500+ }
6501+ if (is_windows) {
6502+ if (mf.section == windows.INVALID_HANDLE_VALUE) switch (windows.ntdll.NtCreateSection(
6503+ &mf.section,
6504+ windows.STANDARD_RIGHTS_REQUIRED | windows.SECTION_QUERY |
6505+ windows.SECTION_MAP_WRITE | windows.SECTION_MAP_READ | windows.SECTION_EXTEND_SIZE,
6506+ null,
6507+ @constCast(&@as(i64, @intCast(aligned_capacity))),
6508+ windows.PAGE_READWRITE,
6509+ windows.SEC_COMMIT,
6510+ mf.file.handle,
6511+ )) {
6512+ .SUCCESS => {},
6513+ else => return error.MemoryMappingNotSupported,
6514+ };
6515+ var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null;
6516+ var contents_len = aligned_capacity;
6517+ switch (windows.ntdll.NtMapViewOfSection(
6518+ mf.section,
6519+ windows.GetCurrentProcess(),
6520+ @ptrCast(&contents_ptr),
6521+ null,
6522+ 0,
6523+ null,
6524+ &contents_len,
6525+ .ViewUnmap,
6526+ 0,
6527+ windows.PAGE_READWRITE,
6528+ )) {
6529+ .SUCCESS => mf.contents = contents_ptr.?[0..contents_len],
6530+ else => return error.MemoryMappingNotSupported,
6531+ }
6532+ } else mf.contents = try std.posix.mmap(
6533+ null,
6534+ aligned_capacity,
6535+ std.posix.PROT.READ | std.posix.PROT.WRITE,
6536+ .{ .TYPE = if (is_linux) .SHARED_VALIDATE else .SHARED },
6537+ mf.file.handle,
6538+ 0,
6539+ );
6540+}
6541+
6542+pub fn unmap(mf: *MappedFile) void {
6543+ if (mf.contents.len == 0) return;
6544+ if (is_windows)
6545+ _ = windows.ntdll.NtUnmapViewOfSection(windows.GetCurrentProcess(), mf.contents.ptr)
6546+ else
6547+ std.posix.munmap(mf.contents);
6548+ mf.contents = &.{};
6549+ if (is_windows and mf.section != windows.INVALID_HANDLE_VALUE) {
6550+ windows.CloseHandle(mf.section);
6551+ mf.section = windows.INVALID_HANDLE_VALUE;
6552+ }
6553+}
6554+
6555+fn verify(mf: *MappedFile) void {
6556+ const root = Node.Index.root.get(mf);
6557+ assert(root.parent == .none);
6558+ assert(root.prev == .none);
6559+ assert(root.next == .none);
6560+ mf.verifyNode(Node.Index.root);
6561+}
6562+
6563+fn verifyNode(mf: *MappedFile, parent_ni: Node.Index) void {
6564+ const parent = parent_ni.get(mf);
6565+ const parent_offset, const parent_size = parent.location().resolve(mf);
6566+ var prev_ni: Node.Index = .none;
6567+ var prev_end: u64 = 0;
6568+ var ni = parent.first;
6569+ while (true) {
6570+ if (ni == .none) {
6571+ assert(parent.last == prev_ni);
6572+ return;
6573+ }
6574+ const node = ni.get(mf);
6575+ assert(node.parent == parent_ni);
6576+ const offset, const size = node.location().resolve(mf);
6577+ assert(node.flags.alignment.check(@intCast(offset)));
6578+ assert(node.flags.alignment.check(@intCast(size)));
6579+ const end = offset + size;
6580+ assert(end <= parent_offset + parent_size);
6581+ assert(offset >= prev_end);
6582+ assert(node.prev == prev_ni);
6583+ mf.verifyNode(ni);
6584+ prev_ni = ni;
6585+ prev_end = end;
6586+ ni = node.next;
6587+ }
6588+}
6589+
6590+const assert = std.debug.assert;
6591+const builtin = @import("builtin");
6592+const is_linux = builtin.os.tag == .linux;
6593+const is_windows = builtin.os.tag == .windows;
6594+const linux = std.os.linux;
6595+const MappedFile = @This();
6596+const std = @import("std");
6597+const windows = std.os.windows;
6598diff --git a/src/link/Queue.zig b/src/link/Queue.zig
6599index 9f4535e1fe4ef7c6458ee94b571b4157ba422dc8..742b4664f178955b4668f130ebbacab0b9ac2550 100644
6600--- a/src/link/Queue.zig
6601+++ b/src/link/Queue.zig
6602@@ -22,17 +22,17 @@ prelink_wait_count: u32,
6603
6604 /// Prelink tasks which have been enqueued and are not yet owned by the worker thread.
6605 /// Allocated into `gpa`, guarded by `mutex`.
6606-queued_prelink: std.ArrayListUnmanaged(PrelinkTask),
6607+queued_prelink: std.ArrayList(PrelinkTask),
6608 /// The worker thread moves items from `queued_prelink` into this array in order to process them.
6609 /// Allocated into `gpa`, accessed only by the worker thread.
6610-wip_prelink: std.ArrayListUnmanaged(PrelinkTask),
6611+wip_prelink: std.ArrayList(PrelinkTask),
6612
6613 /// Like `queued_prelink`, but for ZCU tasks.
6614 /// Allocated into `gpa`, guarded by `mutex`.
6615-queued_zcu: std.ArrayListUnmanaged(ZcuTask),
6616+queued_zcu: std.ArrayList(ZcuTask),
6617 /// Like `wip_prelink`, but for ZCU tasks.
6618 /// Allocated into `gpa`, accessed only by the worker thread.
6619-wip_zcu: std.ArrayListUnmanaged(ZcuTask),
6620+wip_zcu: std.ArrayList(ZcuTask),
6621
6622 /// When processing ZCU link tasks, we might have to block due to unpopulated MIR. When this
6623 /// happens, some tasks in `wip_zcu` have been run, and some are still pending. This is the
6624@@ -213,32 +213,41 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!
6625
6626 fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
6627 q.flush_safety.lock(); // every `return` site should unlock this before unlocking `q.mutex`
6628-
6629 if (std.debug.runtime_safety) {
6630 q.mutex.lock();
6631 defer q.mutex.unlock();
6632 assert(q.state == .running);
6633 }
6634+
6635+ var have_idle_tasks = true;
6636 prelink: while (true) {
6637 assert(q.wip_prelink.items.len == 0);
6638- {
6639- q.mutex.lock();
6640- defer q.mutex.unlock();
6641- std.mem.swap(std.ArrayListUnmanaged(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
6642- if (q.wip_prelink.items.len == 0) {
6643- if (q.prelink_wait_count == 0) {
6644- break :prelink; // prelink is done
6645- } else {
6646+ swap_queues: while (true) {
6647+ {
6648+ q.mutex.lock();
6649+ defer q.mutex.unlock();
6650+ std.mem.swap(std.ArrayList(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
6651+ if (q.wip_prelink.items.len > 0) break :swap_queues;
6652+ if (q.prelink_wait_count == 0) break :prelink; // prelink is done
6653+ if (!have_idle_tasks) {
6654 // We're expecting more prelink tasks so can't move on to ZCU tasks.
6655 q.state = .finished;
6656 q.flush_safety.unlock();
6657 return;
6658 }
6659 }
6660+ have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
6661+ error.OutOfMemory => have_idle_tasks: {
6662+ comp.link_diags.setAllocFailure();
6663+ break :have_idle_tasks false;
6664+ },
6665+ error.LinkFailure => false,
6666+ };
6667 }
6668 for (q.wip_prelink.items) |task| {
6669 link.doPrelinkTask(comp, task);
6670 }
6671+ have_idle_tasks = true;
6672 q.wip_prelink.clearRetainingCapacity();
6673 }
6674
6675@@ -256,17 +265,29 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
6676
6677 // Now we can run ZCU tasks.
6678 while (true) {
6679- if (q.wip_zcu.items.len == q.wip_zcu_idx) {
6680+ if (q.wip_zcu.items.len == q.wip_zcu_idx) swap_queues: {
6681 q.wip_zcu.clearRetainingCapacity();
6682 q.wip_zcu_idx = 0;
6683- q.mutex.lock();
6684- defer q.mutex.unlock();
6685- std.mem.swap(std.ArrayListUnmanaged(ZcuTask), &q.queued_zcu, &q.wip_zcu);
6686- if (q.wip_zcu.items.len == 0) {
6687- // We've exhausted all available tasks.
6688- q.state = .finished;
6689- q.flush_safety.unlock();
6690- return;
6691+ while (true) {
6692+ {
6693+ q.mutex.lock();
6694+ defer q.mutex.unlock();
6695+ std.mem.swap(std.ArrayList(ZcuTask), &q.queued_zcu, &q.wip_zcu);
6696+ if (q.wip_zcu.items.len > 0) break :swap_queues;
6697+ if (!have_idle_tasks) {
6698+ // We've exhausted all available tasks.
6699+ q.state = .finished;
6700+ q.flush_safety.unlock();
6701+ return;
6702+ }
6703+ }
6704+ have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
6705+ error.OutOfMemory => have_idle_tasks: {
6706+ comp.link_diags.setAllocFailure();
6707+ break :have_idle_tasks false;
6708+ },
6709+ error.LinkFailure => false,
6710+ };
6711 }
6712 }
6713 const task = q.wip_zcu.items[q.wip_zcu_idx];
6714@@ -274,8 +295,18 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
6715 pending: {
6716 if (task != .link_func) break :pending;
6717 const status_ptr = &task.link_func.mir.status;
6718- // First check without the mutex to optimize for the common case where MIR is ready.
6719- if (status_ptr.load(.acquire) != .pending) break :pending;
6720+ while (true) {
6721+ // First check without the mutex to optimize for the common case where MIR is ready.
6722+ if (status_ptr.load(.acquire) != .pending) break :pending;
6723+ if (have_idle_tasks) have_idle_tasks = link.doIdleTask(comp, tid) catch |err| switch (err) {
6724+ error.OutOfMemory => have_idle_tasks: {
6725+ comp.link_diags.setAllocFailure();
6726+ break :have_idle_tasks false;
6727+ },
6728+ error.LinkFailure => false,
6729+ };
6730+ if (!have_idle_tasks) break;
6731+ }
6732 q.mutex.lock();
6733 defer q.mutex.unlock();
6734 if (status_ptr.load(.acquire) != .pending) break :pending;
6735@@ -298,6 +329,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void {
6736 }
6737 }
6738 q.wip_zcu_idx += 1;
6739+ have_idle_tasks = true;
6740 }
6741 }
6742
6743diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
6744index a125a70aae790e7b750c96304bae1bdb3c86376c..d60b91c30f93695076b8299c7e92d8f734812d32 100644
6745--- a/src/link/Wasm.zig
6746+++ b/src/link/Wasm.zig
6747@@ -4257,7 +4257,14 @@ fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Zcu
6748 const func_table_fixups_start: u32 = @intCast(wasm.func_table_fixups.items.len);
6749 wasm.string_bytes_lock.lock();
6750
6751- try codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &wasm.string_bytes, .none);
6752+ {
6753+ var aw: std.Io.Writer.Allocating = .fromArrayList(wasm.base.comp.gpa, &wasm.string_bytes);
6754+ defer wasm.string_bytes = aw.toArrayList();
6755+ codegen.generateSymbol(&wasm.base, pt, .unneeded, .fromInterned(ip_index), &aw.writer, .none) catch |err| switch (err) {
6756+ error.WriteFailed => return error.OutOfMemory,
6757+ else => |e| return e,
6758+ };
6759+ }
6760
6761 const code_len: u32 = @intCast(wasm.string_bytes.items.len - code_start);
6762 const relocs_len: u32 = @intCast(wasm.out_relocs.len - relocs_start);
6763diff --git a/src/main.zig b/src/main.zig
6764index 26d44c4463be802e0e7dea899980b28010b8ffc7..9d43e47ea2dbd949122ab7abfdf285ade21bc987 100644
6765--- a/src/main.zig
6766+++ b/src/main.zig
6767@@ -904,7 +904,6 @@ fn buildOutputType(
6768 var mingw_unicode_entry_point: bool = false;
6769 var enable_link_snapshots: bool = false;
6770 var debug_compiler_runtime_libs = false;
6771- var opt_incremental: ?bool = null;
6772 var install_name: ?[]const u8 = null;
6773 var hash_style: link.File.Lld.Elf.HashStyle = .both;
6774 var entitlements: ?[]const u8 = null;
6775@@ -1374,9 +1373,9 @@ fn buildOutputType(
6776 }
6777 } else if (mem.eql(u8, arg, "-fincremental")) {
6778 dev.check(.incremental);
6779- opt_incremental = true;
6780+ create_module.opts.incremental = true;
6781 } else if (mem.eql(u8, arg, "-fno-incremental")) {
6782- opt_incremental = false;
6783+ create_module.opts.incremental = false;
6784 } else if (mem.eql(u8, arg, "--entitlements")) {
6785 entitlements = args_iter.nextOrFatal();
6786 } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
6787@@ -1479,6 +1478,10 @@ fn buildOutputType(
6788 create_module.opts.use_lld = true;
6789 } else if (mem.eql(u8, arg, "-fno-lld")) {
6790 create_module.opts.use_lld = false;
6791+ } else if (mem.eql(u8, arg, "-fnew-linker")) {
6792+ create_module.opts.use_new_linker = true;
6793+ } else if (mem.eql(u8, arg, "-fno-new-linker")) {
6794+ create_module.opts.use_new_linker = false;
6795 } else if (mem.eql(u8, arg, "-fclang")) {
6796 create_module.opts.use_clang = true;
6797 } else if (mem.eql(u8, arg, "-fno-clang")) {
6798@@ -3371,7 +3374,7 @@ fn buildOutputType(
6799 else => false,
6800 };
6801
6802- const incremental = opt_incremental orelse false;
6803+ const incremental = create_module.resolved_options.incremental;
6804 if (debug_incremental and !incremental) {
6805 fatal("--debug-incremental requires -fincremental", .{});
6806 }
6807@@ -3502,7 +3505,6 @@ fn buildOutputType(
6808 .subsystem = subsystem,
6809 .debug_compile_errors = debug_compile_errors,
6810 .debug_incremental = debug_incremental,
6811- .incremental = incremental,
6812 .enable_link_snapshots = enable_link_snapshots,
6813 .install_name = install_name,
6814 .entitlements = entitlements,
6815@@ -4016,6 +4018,8 @@ fn createModule(
6816 error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}),
6817 error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}),
6818 error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}),
6819+ error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}),
6820+ error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}),
6821 };
6822 }
6823
6824diff --git a/src/target.zig b/src/target.zig
6825index e999d2ae22663bcd84a9eb1fc112a317b12a9c02..1cf5c9b08274a55f6cd956ae433e4fb7141e1abd 100644
6826--- a/src/target.zig
6827+++ b/src/target.zig
6828@@ -231,6 +231,13 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
6829 };
6830 }
6831
6832+pub fn hasNewLinkerSupport(ofmt: std.Target.ObjectFormat) bool {
6833+ return switch (ofmt) {
6834+ .elf => true,
6835+ else => false,
6836+ };
6837+}
6838+
6839 /// The set of targets that our own self-hosted backends have robust support for.
6840 /// Used to select between LLVM backend and self-hosted backend when compiling in
6841 /// debug mode. A given target should only return true here if it is passing greater
6842diff --git a/test/incremental/change_exports b/test/incremental/change_exports
6843index 01605bdeeb36e4d51e986526e94c9a36e2cf07fa..e492930031e7fb5d22d68402bc3e59299a084402 100644
6844--- a/test/incremental/change_exports
6845+++ b/test/incremental/change_exports
6846@@ -1,4 +1,4 @@
6847-//#target=x86_64-linux-selfhosted
6848+#target=x86_64-linux-selfhosted
6849 #target=x86_64-linux-cbe
6850 #target=x86_64-windows-cbe
6851
6852diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler
6853index 3e400b854eb60b4a81a3c9b2f0e3511e11ec3b26..699134100eec6942bc2414d29ab7188332f3a45a 100644
6854--- a/test/incremental/change_panic_handler
6855+++ b/test/incremental/change_panic_handler
6856@@ -1,3 +1,4 @@
6857+#target=x86_64-linux-selfhosted
6858 #target=x86_64-linux-cbe
6859 #target=x86_64-windows-cbe
6860 #update=initial version
6861diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit
6862index 9f7eb6c12f10b42d80e998f352ac9a0a1d5d0842..2d068d593eeb77a07b9c249fc0d7542f1cee4c6c 100644
6863--- a/test/incremental/change_panic_handler_explicit
6864+++ b/test/incremental/change_panic_handler_explicit
6865@@ -1,3 +1,4 @@
6866+#target=x86_64-linux-selfhosted
6867 #target=x86_64-linux-cbe
6868 #target=x86_64-windows-cbe
6869 #update=initial version
6870diff --git a/test/incremental/change_struct_same_fields b/test/incremental/change_struct_same_fields
6871index 97049a1fc03ae2d9dfbdd07e6c06a9e756f133aa..f3bfbbdd6922a0266e42c41f8e92cc96e15bc9fc 100644
6872--- a/test/incremental/change_struct_same_fields
6873+++ b/test/incremental/change_struct_same_fields
6874@@ -1,4 +1,4 @@
6875-//#target=x86_64-linux-selfhosted
6876+#target=x86_64-linux-selfhosted
6877 #target=x86_64-linux-cbe
6878 #target=x86_64-windows-cbe
6879 #target=wasm32-wasi-selfhosted
6880diff --git a/test/incremental/type_becomes_comptime_only b/test/incremental/type_becomes_comptime_only
6881index 5c5d3329750eadc813f892bce6e05234ad563242..3bcae1cd211281cc29d4b970240174c40613fdd6 100644
6882--- a/test/incremental/type_becomes_comptime_only
6883+++ b/test/incremental/type_becomes_comptime_only
6884@@ -1,3 +1,4 @@
6885+#target=x86_64-linux-selfhosted
6886 #target=x86_64-linux-cbe
6887 #target=x86_64-windows-cbe
6888 #target=wasm32-wasi-selfhosted