authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-06-05 09:19:59+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-06-05 09:19:59+02:00
log7b31e360a1c000e1e050a00c9b9bff290c7f3d97
treefc5064dc235062ec7e31a0d0511cce087716e9e9
parent9dd8ae35364e735ec507ce0f4cfa8468adef830f
parent82e8ed66ecaa3fc88267bfcb2850c50fe9a4b955

Merge pull request 'compiler: random assortment of linker-related enhancements' (#35581) from elf2 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35581

16 files changed, 186 insertions(+), 148 deletions(-)

src/Compilation.zig+17-6
......@@ -3344,15 +3344,15 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33443344 comp.time_report.?.stats.real_ns_llvm_emit = ns;
33453345 };
33463346
3347 const zcu_obj_path: ?Cache.Path = if (comp.bin_file != null) p: {
3348 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
3349 } else null;
3350
33473351 llvm_object.emit(pt, .{
33483352 .pre_ir_path = comp.verbose_llvm_ir,
33493353 .pre_bc_path = comp.verbose_llvm_bc,
33503354
3351 .bin_path = p: {
3352 const lf = comp.bin_file orelse break :p null;
3353 const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?);
3354 break :p try p.toStringZ(arena);
3355 },
3355 .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null,
33563356 .asm_path = p: {
33573357 const raw = comp.emit_asm orelse break :p null;
33583358 const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
......@@ -3379,6 +3379,17 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33793379 error.AlreadyReported => {},
33803380 error.OutOfMemory => |e| return e,
33813381 };
3382
3383 if (zcu_obj_path) |path| {
3384 // Tell the linker backend about the ZCU object emitted by LLVM.
3385 link.doPrelinkTask(comp, .{ .load_object = path });
3386 // `link.Queue` has not called `prelink` because it knew we would want to send that
3387 // final link input. It is *our* responsibility to call `prelink` now we're done.
3388 comp.bin_file.?.prelink() catch |err| switch (err) {
3389 error.AlreadyReported => return,
3390 else => |e| return e,
3391 };
3392 }
33823393 }
33833394 }
33843395 if (comp.bin_file) |lf| {
......@@ -3390,7 +3401,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33903401 };
33913402 // This is needed before reading the error flags.
33923403 lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
3393 error.AlreadyReported => {},
3404 error.AlreadyReported => return,
33943405 error.OutOfMemory, error.Canceled => |e| return e,
33953406 };
33963407 }
src/Compilation/Config.zig+10-6
......@@ -130,7 +130,7 @@ pub const ResolveError = error{
130130 ZigLacksTargetSupport,
131131 EmittingBinaryRequiresLlvmLibrary,
132132 LldIncompatibleObjectFormat,
133 LldCannotIncrementallyLink,
133 LldIncompatibleWithSelfHostedBackend,
134134 LtoRequiresLld,
135135 SanitizeThreadRequiresLibCpp,
136136 LibCRequiresLibUnwind,
......@@ -400,15 +400,19 @@ pub fn resolve(options: Options) ResolveError!Config {
400400 break :b true;
401401 }
402402
403 // If there's no ZCU we aren't using the LLVM backend but
404 // it shouldn't influence which linker we pick
405 if (!use_llvm and options.have_zcu) {
406 if (options.use_lld == true) return error.LldCannotIncrementallyLink;
403 // If we have Zig code (i.e. a ZCU) and are compiling with a self-hosted backend, then we
404 // also need to use a self-hosted linker.
405 if (options.have_zcu and !use_llvm) {
406 if (options.use_lld == true) return error.LldIncompatibleWithSelfHostedBackend;
407407 break :b false;
408408 }
409409
410410 if (options.use_lld) |x| break :b x;
411411
412 // If the user didn't specify whether to use LLD but did specify to use the new linker,
413 // assume no LLD.
414 if (options.use_new_linker == true) break :b false;
415
412416 // If we have no zig code to compile, no need for the self-hosted linker.
413417 if (!options.have_zcu) break :b true;
414418
......@@ -437,7 +441,7 @@ pub fn resolve(options: Options) ResolveError!Config {
437441 break :b false;
438442 }
439443
440 if (!target_util.hasNewLinkerSupport(target.ofmt, backend)) {
444 if (!target_util.hasNewLinker(target.ofmt)) {
441445 if (options.use_new_linker == true) return error.NewLinkerIncompatibleObjectFormat;
442446 break :b false;
443447 }
src/codegen/llvm.zig+14
......@@ -518,6 +518,12 @@ pub const Object = struct {
518518 gpa: Allocator,
519519 builder: Builder,
520520
521 /// The basename of the object file which will emitted by LLVM for the ZCU. Once it it emitted,
522 /// this object file is passed to the active linker implementation as an ordinary link input.
523 ///
524 /// For the full path, use `Compilation.resolveEmitPath` with `kind == .temp`.
525 out_bin_basename: []const u8,
526
521527 /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes:
522528 ///
523529 /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a
......@@ -657,6 +663,14 @@ pub const Object = struct {
657663 obj.* = .{
658664 .gpa = gpa,
659665 .builder = builder,
666 .out_bin_basename = try std.zig.binNameAlloc(arena, .{
667 .root_name = try std.fmt.allocPrint(arena, "{s}_zcu", .{comp.root_name}),
668 .cpu_arch = target.cpu.arch,
669 .os_tag = target.os.tag,
670 .ofmt = target.ofmt,
671 .abi = target.abi,
672 .output_mode = .Obj,
673 }),
660674 .type_pool = .empty,
661675 .lazy_abi_aligns = .empty,
662676 .debug_compile_unit = debug_compile_unit,
src/link.zig+15-13
......@@ -402,11 +402,6 @@ pub const File = struct {
402402 emit: Path,
403403
404404 file: ?Io.File,
405 /// When using the LLVM backend, the emitted object is written to a file with this name. This
406 /// object file then becomes a normal link input to LLD or a self-hosted linker.
407 ///
408 /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`).
409 zcu_object_basename: ?[]const u8 = null,
410405 gc_sections: bool,
411406 print_gc_sections: bool,
412407 build_id: std.zig.BuildId,
......@@ -1196,20 +1191,22 @@ pub const File = struct {
11961191 /// Called when all linker inputs have been sent via `loadInput`. After
11971192 /// this, `loadInput` will not be called anymore.
11981193 pub fn prelink(base: *File) Error!void {
1199 assert(!base.post_prelink);
1200
1201 // In this case, an object file is created by the LLVM backend, so
1202 // there is no prelink phase. The Zig code is linked as a standard
1203 // object along with the others.
1204 if (base.zcu_object_basename != null) return;
1194 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with
1195 // `-fincremental`. This works only because `File.Lld` does nothing in prelink.
1196 // Related: https://codeberg.org/ziglang/zig/issues/32081
1197 if (base.tag != .lld) {
1198 assert(!base.post_prelink);
1199 }
12051200
12061201 switch (base.tag) {
12071202 inline .elf2, .coff2, .wasm => |tag| {
12081203 dev.check(tag.devFeature());
1209 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
1204 try @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
12101205 },
12111206 else => {},
12121207 }
1208
1209 base.post_prelink = true;
12131210 }
12141211
12151212 /// Legacy function for old linker code
......@@ -1407,7 +1404,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
14071404 return;
14081405 };
14091406
1410 assert(!base.post_prelink);
1407 // The guard on this assertion is a temporary hack to make the LLVM backend with LLD work with
1408 // `-fincremental`. This works only because `File.Lld` does nothing in prelink.
1409 // Related: https://codeberg.org/ziglang/zig/issues/32081
1410 if (base.tag != .lld) {
1411 assert(!base.post_prelink);
1412 }
14111413
14121414 var timer = comp.startTimer();
14131415 defer if (timer.finish(io)) |ns| {
src/link/Coff.zig+1-1
......@@ -1524,7 +1524,7 @@ pub fn addReloc(
15241524 target.target_relocs = ri;
15251525}
15261526
1527pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) void {
1527pub fn prelink(coff: *Coff, prog_node: std.Progress.Node) link.Error!void {
15281528 _ = coff;
15291529 _ = prog_node;
15301530}
src/link/Elf.zig+2-31
......@@ -260,10 +260,6 @@ pub fn createEmpty(
260260 .tag = .elf,
261261 .comp = comp,
262262 .emit = emit,
263 .zcu_object_basename = if (use_llvm)
264 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
265 else
266 null,
267263 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
268264 .print_gc_sections = options.print_gc_sections,
269265 .stack_size = options.stack_size orelse 16777216,
......@@ -762,18 +758,14 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std
762758}
763759
764760fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
761 _ = arena;
762
765763 const comp = self.base.comp;
766764 const gpa = comp.gpa;
767765 const diags = &comp.link_diags;
768766
769 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
770 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
771 } else null;
772
773767 if (self.zigObjectPtr()) |zig_object| try zig_object.flush(self, tid);
774768
775 if (zcu_obj_path) |path| openParseObjectReportingFailure(self, path);
776
777769 switch (comp.config.output_mode) {
778770 .Obj => return relocatable.flushObject(self, comp),
779771 .Lib => switch (comp.config.link_mode) {
......@@ -1046,27 +1038,6 @@ fn dumpArgvInit(self: *Elf, arena: Allocator) !void {
10461038 }
10471039}
10481040
1049pub fn openParseObjectReportingFailure(self: *Elf, path: Path) void {
1050 const comp = self.base.comp;
1051 const io = comp.io;
1052 const diags = &comp.link_diags;
1053 const obj = link.openObject(io, path, false, false) catch |err| {
1054 switch (diags.failParse(path, "failed to open object: {t}", .{err})) {
1055 error.AlreadyReported => return,
1056 }
1057 };
1058 self.parseObjectReportingFailure(obj);
1059}
1060
1061fn parseObjectReportingFailure(self: *Elf, obj: link.Input.Object) void {
1062 const comp = self.base.comp;
1063 const diags = &comp.link_diags;
1064 self.parseObject(obj) catch |err| switch (err) {
1065 error.AlreadyReported => return, // already reported
1066 else => |e| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}),
1067 };
1068}
1069
10701041fn parseObject(self: *Elf, obj: link.Input.Object) !void {
10711042 const tracy = trace(@src());
10721043 defer tracy.end();
src/link/Elf2.zig+24-20
......@@ -4825,25 +4825,29 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
48254825fn prelinkInner(elf: *Elf) Error!void {
48264826 const comp = elf.base.comp;
48274827 const gpa = comp.gpa;
4828 try elf.ensureUnusedSymbolCapacity(1, .all_local);
4829 try elf.inputs.ensureUnusedCapacity(gpa, 1);
4830 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
4831 std.fs.path.stem(elf.base.emit.sub_path),
4832 });
4833 defer gpa.free(zcu_name);
4834 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
4835 .node = .none,
4836 .name = try elf.string(.strtab, zcu_name),
4837 .value = 0,
4838 .size = 0,
4839 .type = .FILE,
4840 .shndx = .ABS,
4841 });
4842 elf.inputs.addOneAssumeCapacity().* = .{
4843 .path = elf.base.emit,
4844 .member = null,
4845 .file_symbol = zcu_file_symbol,
4846 };
4828
4829 if (comp.zcu != null and !comp.config.use_llvm) {
4830 // We're use self-hosted codegen---add an input representing the Zig "object".
4831 try elf.ensureUnusedSymbolCapacity(1, .all_local);
4832 try elf.inputs.ensureUnusedCapacity(gpa, 1);
4833 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
4834 std.fs.path.stem(elf.base.emit.sub_path),
4835 });
4836 defer gpa.free(zcu_name);
4837 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
4838 .node = .none,
4839 .name = try elf.string(.strtab, zcu_name),
4840 .value = 0,
4841 .size = 0,
4842 .type = .FILE,
4843 .shndx = .ABS,
4844 });
4845 elf.inputs.addOneAssumeCapacity().* = .{
4846 .path = elf.base.emit,
4847 .member = null,
4848 .file_symbol = zcu_file_symbol,
4849 };
4850 }
48474851
48484852 if (elf.shndx.dynamic != .UNDEF) switch (elf.identClass()) {
48494853 .NONE, _ => unreachable,
......@@ -5859,8 +5863,8 @@ pub fn flush(
58595863) link.Error!void {
58605864 const comp = elf.base.comp;
58615865 const diags = &comp.link_diags;
5862 _ = arena;
58635866 _ = prog_node;
5867 _ = arena;
58645868
58655869 if (comp.config.output_mode == .Exe) {
58665870 var any_undef = false;
src/link/Lld.zig+8-14
......@@ -207,11 +207,6 @@ pub fn createEmpty(
207207 const output_mode = comp.config.output_mode;
208208 const optimize_mode = comp.root_mod.optimize_mode;
209209
210 const obj_file_ext: []const u8 = switch (target.ofmt) {
211 .coff => "obj",
212 .elf, .wasm => "o",
213 else => unreachable,
214 };
215210 const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) {
216211 .coff => optimize_mode != .Debug,
217212 .elf => optimize_mode != .Debug and output_mode != .Obj,
......@@ -230,7 +225,6 @@ pub fn createEmpty(
230225 .tag = .lld,
231226 .comp = comp,
232227 .emit = emit,
233 .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }),
234228 .gc_sections = gc_sections,
235229 .print_gc_sections = options.print_gc_sections,
236230 .stack_size = stack_size,
......@@ -290,8 +284,8 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
290284 const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0);
291285 const opt_zcu = comp.zcu;
292286
293 const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: {
294 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
287 const zcu_obj_path: ?Cache.Path = if (opt_zcu) |zcu| p: {
288 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
295289 } else null;
296290
297291 log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
......@@ -376,8 +370,8 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
376370 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
377371 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
378372
379 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
380 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
373 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
374 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
381375 } else null;
382376
383377 const is_lib = comp.config.output_mode == .Lib;
......@@ -766,8 +760,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
766760 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
767761 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
768762
769 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
770 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
763 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
764 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
771765 } else null;
772766
773767 const output_mode = comp.config.output_mode;
......@@ -1364,8 +1358,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
13641358 const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
13651359 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
13661360
1367 const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
1368 break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
1361 const zcu_obj_path: ?Cache.Path = if (comp.zcu) |zcu| p: {
1362 break :p try comp.resolveEmitPathFlush(arena, .temp, zcu.llvm_object.?.out_bin_basename);
13691363 } else null;
13701364
13711365 const is_obj = comp.config.output_mode == .Obj;
src/link/MachO.zig+10-10
......@@ -181,10 +181,6 @@ pub fn createEmpty(
181181 .tag = .macho,
182182 .comp = comp,
183183 .emit = emit,
184 .zcu_object_basename = if (use_llvm)
185 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
186 else
187 null,
188184 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
189185 .print_gc_sections = options.print_gc_sections,
190186 .stack_size = options.stack_size orelse 16777216,
......@@ -353,9 +349,11 @@ pub fn flush(
353349 const sub_prog_node = prog_node.start("MachO Flush", 0);
354350 defer sub_prog_node.end();
355351
356 const zcu_obj_path: ?Path = if (self.base.zcu_object_basename) |raw| p: {
357 break :p try comp.resolveEmitPathFlush(arena, .temp, raw);
358 } else null;
352 const zcu_obj_path: ?Path = p: {
353 const zcu = comp.zcu orelse break :p null;
354 const llvm_object = zcu.llvm_object orelse break :p null;
355 break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
356 };
359357
360358 // --verbose-link
361359 if (comp.verbose_link) try self.dumpArgv(comp);
......@@ -630,10 +628,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
630628
631629 const directory = self.base.emit.root_dir;
632630 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
633 const zcu_obj_path: ?[]const u8 = if (self.base.zcu_object_basename) |raw| p: {
634 const p = try comp.resolveEmitPathFlush(arena, .temp, raw);
631 const zcu_obj_path: ?[]const u8 = p: {
632 const zcu = comp.zcu orelse break :p null;
633 const llvm_object = zcu.llvm_object orelse break :p null;
634 const p = try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename);
635635 break :p try p.toString(arena);
636 } else null;
636 };
637637
638638 var argv = std.array_list.Managed([]const u8).init(arena);
639639
src/link/Queue.zig+19-13
......@@ -130,14 +130,17 @@ pub fn finishPrelinkQueue(q: *Queue, comp: *Compilation) Io.Cancelable!void {
130130 prelink: {
131131 const lf = comp.bin_file orelse break :prelink;
132132 if (lf.post_prelink) break :prelink;
133 if (comp.zcu != null and comp.zcu.?.llvm_object != null) {
134 // Don't call `prelink` just yet. It will be the frontend's responsibility instead,
135 // after it sends the ZCU object emitted by LLVM as the final link input.
136 break :prelink;
137 }
133138
134 if (lf.prelink()) |_| {
135 lf.post_prelink = true;
136 } else |err| switch (err) {
139 lf.prelink() catch |err| switch (err) {
137140 error.OutOfMemory => comp.link_diags.setAllocFailure(),
138141 error.AlreadyReported => {},
139142 error.Canceled => |e| return e,
140 }
143 };
141144 }
142145}
143146
......@@ -171,16 +174,19 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) void {
171174 }
172175
173176 // We've finished the prelink tasks, so run prelink if necessary.
174 if (comp.bin_file) |lf| {
175 if (!lf.post_prelink) {
176 if (lf.prelink()) |_| {
177 lf.post_prelink = true;
178 } else |err| switch (err) {
179 error.OutOfMemory => comp.link_diags.setAllocFailure(),
180 error.Canceled => @panic("TODO"),
181 error.AlreadyReported => {},
182 }
177 prelink: {
178 const lf = comp.bin_file orelse break :prelink;
179 if (lf.post_prelink) break :prelink;
180 if (comp.zcu != null and comp.zcu.?.llvm_object != null) {
181 // Don't call `prelink` just yet. It will be the frontend's responsibility instead,
182 // after it sends the ZCU object emitted by LLVM as the final link input.
183 break :prelink;
183184 }
185 lf.prelink() catch |err| switch (err) {
186 error.OutOfMemory => comp.link_diags.setAllocFailure(),
187 error.Canceled => @panic("TODO"),
188 error.AlreadyReported => {},
189 };
184190 }
185191
186192 zcu_tasks: while (true) {
src/link/Wasm.zig+1-27
......@@ -2944,7 +2944,6 @@ pub fn createEmpty(
29442944 const target = &comp.root_mod.resolved_target.result;
29452945 assert(target.ofmt == .wasm);
29462946
2947 const use_llvm = comp.config.use_llvm;
29482947 const output_mode = comp.config.output_mode;
29492948 const wasi_exec_model = comp.config.wasi_exec_model;
29502949
......@@ -2954,10 +2953,6 @@ pub fn createEmpty(
29542953 .tag = .wasm,
29552954 .comp = comp,
29562955 .emit = emit,
2957 .zcu_object_basename = if (use_llvm)
2958 try std.fmt.allocPrint(arena, "{s}_zcu.o", .{fs.path.stem(emit.sub_path)})
2959 else
2960 null,
29612956 // Garbage collection is so crucial to WebAssembly that we design
29622957 // the linker around the assumption that it will be on in the vast
29632958 // majority of cases, and therefore express "no garbage collection"
......@@ -3021,22 +3016,6 @@ pub fn createEmpty(
30213016 return wasm;
30223017}
30233018
3024fn openParseObjectReportingFailure(wasm: *Wasm, path: Path) void {
3025 const comp = wasm.base.comp;
3026 const io = comp.io;
3027 const diags = &comp.link_diags;
3028 const obj = link.openObject(io, path, false, false) catch |err| {
3029 switch (diags.failParse(path, "failed to open object: {t}", .{err})) {
3030 error.AlreadyReported => return,
3031 }
3032 };
3033 wasm.parseObject(obj) catch |err| {
3034 switch (diags.failParse(path, "failed to parse object: {t}", .{err})) {
3035 error.AlreadyReported => return,
3036 }
3037 };
3038}
3039
30403019fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
30413020 log.debug("parseObject {f}", .{obj.path});
30423021 const gpa = wasm.base.comp.gpa;
......@@ -3822,6 +3801,7 @@ pub fn flush(
38223801 tid: Zcu.PerThread.Id,
38233802 prog_node: std.Progress.Node,
38243803) link.Error!void {
3804 _ = arena;
38253805 // The goal is to never use this because it's only needed if we need to
38263806 // write to InternPool, but flush is too late to be writing to the
38273807 // InternPool.
......@@ -3833,12 +3813,6 @@ pub fn flush(
38333813
38343814 if (comp.verbose_link) try Compilation.dumpArgv(io, wasm.dump_argv_list.items);
38353815
3836 if (wasm.base.zcu_object_basename) |raw| {
3837 const zcu_obj_path: Path = try comp.resolveEmitPathFlush(arena, .temp, raw);
3838 openParseObjectReportingFailure(wasm, zcu_obj_path);
3839 try prelink(wasm, prog_node);
3840 }
3841
38423816 const tracy = trace(@src());
38433817 defer tracy.end();
38443818
src/main.zig+2-2
......@@ -4190,7 +4190,7 @@ fn createModule(
41904190 error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}),
41914191 error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}),
41924192 error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}),
4193 error.LldCannotIncrementallyLink => fatal("self-hosted backends do not support linking with LLD", .{}),
4193 error.LldIncompatibleWithSelfHostedBackend => fatal("self-hosted backends do not support linking with LLD", .{}),
41944194 error.LtoRequiresLld => fatal("LTO requires using LLD", .{}),
41954195 error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}),
41964196 error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}),
......@@ -4212,8 +4212,8 @@ fn createModule(
42124212 error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}),
42134213 error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}),
42144214 error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}),
4215 error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}),
42164215 error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}),
4216 error.NewLinkerIncompatibleObjectFormat => fatal("no new linker available for '{t}' files", .{target.ofmt}),
42174217 };
42184218 }
42194219
src/target.zig+3-5
......@@ -275,12 +275,10 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool {
275275 };
276276}
277277
278pub fn hasNewLinkerSupport(ofmt: std.Target.ObjectFormat, backend: std.lang.CompilerBackend) bool {
278/// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful.
279pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool {
279280 return switch (ofmt) {
280 .elf, .coff => switch (backend) {
281 .stage2_x86_64 => true,
282 else => false,
283 },
281 .elf => true,
284282 else => false,
285283 };
286284}
test/standalone/build.zig.zon+3
......@@ -193,6 +193,9 @@
193193 .debug_io_color = .{
194194 .path = "debug_io_color",
195195 },
196 .elf2 = .{
197 .path = "elf2",
198 },
196199 },
197200 .paths = .{
198201 "build.zig",
test/standalone/elf2/build.zig created+51
......@@ -0,0 +1,51 @@
1pub fn build(b: *Build) void {
2 const test_step = b.step("test", "Test the new ELF linker");
3 b.default_step = test_step;
4
5 if (b.graph.host.result.cpu.arch == .x86_64 and b.graph.host.result.os.tag == .linux) {
6 addOne(b, test_step, b.graph.host, false, .static, "elf2-hello-native-selfhosted-static");
7 addOne(b, test_step, b.graph.host, false, .dynamic, "elf2-hello-native-selfhosted-dynamic");
8 addOne(b, test_step, b.graph.host, true, .static, "elf2-hello-native-llvm-static");
9 addOne(b, test_step, b.graph.host, true, .dynamic, "elf2-hello-native-llvm-dynamic");
10 }
11
12 const x86_64_linux_target: Build.ResolvedTarget = b.resolveTargetQuery(.{
13 .cpu_arch = .x86_64,
14 .os_tag = .linux,
15 });
16 addOne(b, test_step, x86_64_linux_target, false, .static, "elf2-hello-selfhosted-static");
17 addOne(b, test_step, x86_64_linux_target, true, .static, "elf2-hello-llvm-static");
18}
19
20fn addOne(
21 b: *Build,
22 test_step: *Build.Step,
23 target: Build.ResolvedTarget,
24 use_llvm: bool,
25 link_mode: std.lang.LinkMode,
26 name: []const u8,
27) void {
28 const mod = b.createModule(.{
29 .root_source_file = b.path("hello.zig"),
30 .target = target,
31 .optimize = .Debug,
32 .link_libc = link_mode == .dynamic,
33 });
34 const exe = b.addExecutable(.{
35 .name = name,
36 .root_module = mod,
37 .linkage = link_mode,
38 });
39 exe.use_new_linker = true;
40 exe.use_llvm = use_llvm;
41
42 const run = b.addRunArtifact(exe);
43 run.expectExitCode(0);
44 run.expectStdOutEqual("Hello, World!\n");
45 run.skip_foreign_checks = true;
46
47 test_step.dependOn(&run.step);
48}
49
50const std = @import("std");
51const Build = std.Build;
test/standalone/elf2/hello.zig created+6
......@@ -0,0 +1,6 @@
1pub fn main(init: std.process.Init) !void {
2 const stdout: std.Io.File = .stdout();
3 try stdout.writeStreamingAll(init.io, "Hello, World!\n");
4}
5
6const std = @import("std");