authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-04-27 03:47:58+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-04-27 03:52:52+02:00
log30e254fc311875720466d9a2b19d06e5599cd8c1
treee0ade21398e53abe485c50d274c9cf8b968c79f8
parent5668c8b7babfdc00ace04feceb9bcf8b95681982
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

link: Stub out GOFF/XCOFF linker code based on LLVM.

This allows emitting object files for s390x-zos (GOFF) and powerpc(64)-aix (XCOFF). Note that GOFF emission in LLVM is still being worked on upstream for LLVM 21; the resulting object files are useless right now. Also, -fstrip is required, or LLVM will SIGSEGV during DWARF emission.

5 files changed, 264 insertions(+), 6 deletions(-)

CMakeLists.txt+2
...@@ -612,6 +612,7 @@ set(ZIG_STAGE2_SOURCES...@@ -612,6 +612,7 @@ set(ZIG_STAGE2_SOURCES
612 src/link/Elf/relocatable.zig612 src/link/Elf/relocatable.zig
613 src/link/Elf/relocation.zig613 src/link/Elf/relocation.zig
614 src/link/Elf/synthetic_sections.zig614 src/link/Elf/synthetic_sections.zig
615 src/link/Goff.zig
615 src/link/LdScript.zig616 src/link/LdScript.zig
616 src/link/MachO.zig617 src/link/MachO.zig
617 src/link/MachO/Archive.zig618 src/link/MachO/Archive.zig
...@@ -652,6 +653,7 @@ set(ZIG_STAGE2_SOURCES...@@ -652,6 +653,7 @@ set(ZIG_STAGE2_SOURCES
652 src/link/Wasm/Archive.zig653 src/link/Wasm/Archive.zig
653 src/link/Wasm/Flush.zig654 src/link/Wasm/Flush.zig
654 src/link/Wasm/Object.zig655 src/link/Wasm/Object.zig
656 src/link/Xcoff.zig
655 src/link/aarch64.zig657 src/link/aarch64.zig
656 src/link/riscv.zig658 src/link/riscv.zig
657 src/link/table_section.zig659 src/link/table_section.zig
src/dev.zig+4
...@@ -82,6 +82,8 @@ pub const Env = enum {...@@ -82,6 +82,8 @@ pub const Env = enum {
82 .spirv_linker,82 .spirv_linker,
83 .plan9_linker,83 .plan9_linker,
84 .nvptx_linker,84 .nvptx_linker,
85 .goff_linker,
86 .xcoff_linker,
85 => true,87 => true,
86 .cc_command,88 .cc_command,
87 .translate_c_command,89 .translate_c_command,
...@@ -228,6 +230,8 @@ pub const Feature = enum {...@@ -228,6 +230,8 @@ pub const Feature = enum {
228 spirv_linker,230 spirv_linker,
229 plan9_linker,231 plan9_linker,
230 nvptx_linker,232 nvptx_linker,
233 goff_linker,
234 xcoff_linker,
231};235};
232236
233/// Makes the code following the call to this function unreachable if `feature` is disabled.237/// Makes the code following the call to this function unreachable if `feature` is disabled.
src/link.zig+18-6
...@@ -556,9 +556,9 @@ pub const File = struct {...@@ -556,9 +556,9 @@ pub const File = struct {
556 const comp = base.comp;556 const comp = base.comp;
557 const gpa = comp.gpa;557 const gpa = comp.gpa;
558 switch (base.tag) {558 switch (base.tag) {
559 .coff, .elf, .macho, .plan9, .wasm => {559 .coff, .elf, .macho, .plan9, .wasm, .goff, .xcoff => {
560 if (base.file != null) return;560 if (base.file != null) return;
561 dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker });561 dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
562 const emit = base.emit;562 const emit = base.emit;
563 if (base.child_pid) |pid| {563 if (base.child_pid) |pid| {
564 if (builtin.os.tag == .windows) {564 if (builtin.os.tag == .windows) {
...@@ -650,8 +650,8 @@ pub const File = struct {...@@ -650,8 +650,8 @@ pub const File = struct {
650 }650 }
651 }651 }
652 },652 },
653 .coff, .macho, .plan9, .wasm => if (base.file) |f| {653 .coff, .macho, .plan9, .wasm, .goff, .xcoff => if (base.file) |f| {
654 dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker });654 dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
655 if (base.zcu_object_sub_path != null) {655 if (base.zcu_object_sub_path != null) {
656 // The file we have open is not the final file that we want to656 // The file we have open is not the final file that we want to
657 // make executable, so we don't have to close it.657 // make executable, so we don't have to close it.
...@@ -767,6 +767,7 @@ pub const File = struct {...@@ -767,6 +767,7 @@ pub const File = struct {
767767
768 switch (base.tag) {768 switch (base.tag) {
769 .spirv, .nvptx => {},769 .spirv, .nvptx => {},
770 .goff, .xcoff => {},
770 inline else => |tag| {771 inline else => |tag| {
771 dev.check(tag.devFeature());772 dev.check(tag.devFeature());
772 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);773 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
...@@ -902,6 +903,7 @@ pub const File = struct {...@@ -902,6 +903,7 @@ pub const File = struct {
902 .spirv => unreachable,903 .spirv => unreachable,
903 .nvptx => unreachable,904 .nvptx => unreachable,
904 .wasm => unreachable,905 .wasm => unreachable,
906 .goff, .xcoff => unreachable,
905 inline else => |tag| {907 inline else => |tag| {
906 dev.check(tag.devFeature());908 dev.check(tag.devFeature());
907 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);909 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
...@@ -921,6 +923,7 @@ pub const File = struct {...@@ -921,6 +923,7 @@ pub const File = struct {
921 .spirv => unreachable,923 .spirv => unreachable,
922 .nvptx => unreachable,924 .nvptx => unreachable,
923 .wasm => unreachable,925 .wasm => unreachable,
926 .goff, .xcoff => unreachable,
924 inline else => |tag| {927 inline else => |tag| {
925 dev.check(tag.devFeature());928 dev.check(tag.devFeature());
926 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);929 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
...@@ -934,6 +937,7 @@ pub const File = struct {...@@ -934,6 +937,7 @@ pub const File = struct {
934 .spirv => unreachable,937 .spirv => unreachable,
935 .nvptx => unreachable,938 .nvptx => unreachable,
936 .wasm => unreachable,939 .wasm => unreachable,
940 .goff, .xcoff => unreachable,
937 inline else => |tag| {941 inline else => |tag| {
938 dev.check(tag.devFeature());942 dev.check(tag.devFeature());
939 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);943 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
...@@ -950,6 +954,8 @@ pub const File = struct {...@@ -950,6 +954,8 @@ pub const File = struct {
950 .plan9,954 .plan9,
951 .spirv,955 .spirv,
952 .nvptx,956 .nvptx,
957 .goff,
958 .xcoff,
953 => {},959 => {},
954960
955 inline else => |tag| {961 inline else => |tag| {
...@@ -1246,6 +1252,8 @@ pub const File = struct {...@@ -1246,6 +1252,8 @@ pub const File = struct {
1246 spirv,1252 spirv,
1247 plan9,1253 plan9,
1248 nvptx,1254 nvptx,
1255 goff,
1256 xcoff,
12491257
1250 pub fn Type(comptime tag: Tag) type {1258 pub fn Type(comptime tag: Tag) type {
1251 return switch (tag) {1259 return switch (tag) {
...@@ -1257,6 +1265,8 @@ pub const File = struct {...@@ -1257,6 +1265,8 @@ pub const File = struct {
1257 .spirv => SpirV,1265 .spirv => SpirV,
1258 .plan9 => Plan9,1266 .plan9 => Plan9,
1259 .nvptx => NvPtx,1267 .nvptx => NvPtx,
1268 .goff => Goff,
1269 .xcoff => Xcoff,
1260 };1270 };
1261 }1271 }
12621272
...@@ -1270,8 +1280,8 @@ pub const File = struct {...@@ -1270,8 +1280,8 @@ pub const File = struct {
1270 .c => .c,1280 .c => .c,
1271 .spirv => .spirv,1281 .spirv => .spirv,
1272 .nvptx => .nvptx,1282 .nvptx => .nvptx,
1273 .goff => @panic("TODO implement goff object format"),1283 .goff => .goff,
1274 .xcoff => @panic("TODO implement xcoff object format"),1284 .xcoff => .xcoff,
1275 .hex => @panic("TODO implement hex object format"),1285 .hex => @panic("TODO implement hex object format"),
1276 .raw => @panic("TODO implement raw object format"),1286 .raw => @panic("TODO implement raw object format"),
1277 };1287 };
...@@ -1377,6 +1387,8 @@ pub const File = struct {...@@ -1377,6 +1387,8 @@ pub const File = struct {
1377 pub const SpirV = @import("link/SpirV.zig");1387 pub const SpirV = @import("link/SpirV.zig");
1378 pub const Wasm = @import("link/Wasm.zig");1388 pub const Wasm = @import("link/Wasm.zig");
1379 pub const NvPtx = @import("link/NvPtx.zig");1389 pub const NvPtx = @import("link/NvPtx.zig");
1390 pub const Goff = @import("link/Goff.zig");
1391 pub const Xcoff = @import("link/Xcoff.zig");
1380 pub const Dwarf = @import("link/Dwarf.zig");1392 pub const Dwarf = @import("link/Dwarf.zig");
1381};1393};
13821394
src/link/Goff.zig created+120
...@@ -0,0 +1,120 @@
1//! Stub linker support for GOFF based on LLVM.
2
3const Goff = @This();
4
5const std = @import("std");
6const builtin = @import("builtin");
7
8const Allocator = std.mem.Allocator;
9const assert = std.debug.assert;
10const log = std.log.scoped(.link);
11const Path = std.Build.Cache.Path;
12
13const Zcu = @import("../Zcu.zig");
14const InternPool = @import("../InternPool.zig");
15const Compilation = @import("../Compilation.zig");
16const link = @import("../link.zig");
17const trace = @import("../tracy.zig").trace;
18const build_options = @import("build_options");
19const Air = @import("../Air.zig");
20const Liveness = @import("../Liveness.zig");
21const LlvmObject = @import("../codegen/llvm.zig").Object;
22
23base: link.File,
24llvm_object: LlvmObject.Ptr,
25
26pub fn createEmpty(
27 arena: Allocator,
28 comp: *Compilation,
29 emit: Path,
30 options: link.File.OpenOptions,
31) !*Goff {
32 const target = comp.root_mod.resolved_target.result;
33 const use_lld = build_options.have_llvm and comp.config.use_lld;
34 const use_llvm = comp.config.use_llvm;
35
36 assert(use_llvm); // Caught by Compilation.Config.resolve.
37 assert(!use_lld); // Caught by Compilation.Config.resolve.
38 assert(target.os.tag == .zos); // Caught by Compilation.Config.resolve.
39
40 const llvm_object = try LlvmObject.create(arena, comp);
41 const goff = try arena.create(Goff);
42 goff.* = .{
43 .base = .{
44 .tag = .goff,
45 .comp = comp,
46 .emit = emit,
47 .zcu_object_sub_path = emit.sub_path,
48 .gc_sections = options.gc_sections orelse false,
49 .print_gc_sections = options.print_gc_sections,
50 .stack_size = options.stack_size orelse 0,
51 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
52 .file = null,
53 .disable_lld_caching = options.disable_lld_caching,
54 .build_id = options.build_id,
55 },
56 .llvm_object = llvm_object,
57 };
58
59 return goff;
60}
61
62pub fn open(
63 arena: Allocator,
64 comp: *Compilation,
65 emit: Path,
66 options: link.File.OpenOptions,
67) !*Goff {
68 const target = comp.root_mod.resolved_target.result;
69 assert(target.ofmt == .goff);
70 return createEmpty(arena, comp, emit, options);
71}
72
73pub fn deinit(self: *Goff) void {
74 self.llvm_object.deinit();
75}
76
77pub fn updateFunc(
78 self: *Goff,
79 pt: Zcu.PerThread,
80 func_index: InternPool.Index,
81 air: Air,
82 liveness: Liveness,
83) link.File.UpdateNavError!void {
84 if (build_options.skip_non_native and builtin.object_format != .goff)
85 @panic("Attempted to compile for object format that was disabled by build configuration");
86
87 try self.llvm_object.updateFunc(pt, func_index, air, liveness);
88}
89
90pub fn updateNav(self: *Goff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
91 if (build_options.skip_non_native and builtin.object_format != .goff)
92 @panic("Attempted to compile for object format that was disabled by build configuration");
93
94 return self.llvm_object.updateNav(pt, nav);
95}
96
97pub fn updateExports(
98 self: *Goff,
99 pt: Zcu.PerThread,
100 exported: Zcu.Exported,
101 export_indices: []const Zcu.Export.Index,
102) !void {
103 if (build_options.skip_non_native and builtin.object_format != .goff)
104 @panic("Attempted to compile for object format that was disabled by build configuration");
105
106 return self.llvm_object.updateExports(pt, exported, export_indices);
107}
108
109pub fn flush(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
110 return self.flushModule(arena, tid, prog_node);
111}
112
113pub fn flushModule(self: *Goff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
114 if (build_options.skip_non_native and builtin.object_format != .goff)
115 @panic("Attempted to compile for object format that was disabled by build configuration");
116
117 _ = tid;
118
119 try self.base.emitLlvmObject(arena, self.llvm_object, prog_node);
120}
src/link/Xcoff.zig created+120
...@@ -0,0 +1,120 @@
1//! Stub linker support for GOFF based on LLVM.
2
3const Xcoff = @This();
4
5const std = @import("std");
6const builtin = @import("builtin");
7
8const Allocator = std.mem.Allocator;
9const assert = std.debug.assert;
10const log = std.log.scoped(.link);
11const Path = std.Build.Cache.Path;
12
13const Zcu = @import("../Zcu.zig");
14const InternPool = @import("../InternPool.zig");
15const Compilation = @import("../Compilation.zig");
16const link = @import("../link.zig");
17const trace = @import("../tracy.zig").trace;
18const build_options = @import("build_options");
19const Air = @import("../Air.zig");
20const Liveness = @import("../Liveness.zig");
21const LlvmObject = @import("../codegen/llvm.zig").Object;
22
23base: link.File,
24llvm_object: LlvmObject.Ptr,
25
26pub fn createEmpty(
27 arena: Allocator,
28 comp: *Compilation,
29 emit: Path,
30 options: link.File.OpenOptions,
31) !*Xcoff {
32 const target = comp.root_mod.resolved_target.result;
33 const use_lld = build_options.have_llvm and comp.config.use_lld;
34 const use_llvm = comp.config.use_llvm;
35
36 assert(use_llvm); // Caught by Compilation.Config.resolve.
37 assert(!use_lld); // Caught by Compilation.Config.resolve.
38 assert(target.os.tag == .aix); // Caught by Compilation.Config.resolve.
39
40 const llvm_object = try LlvmObject.create(arena, comp);
41 const xcoff = try arena.create(Xcoff);
42 xcoff.* = .{
43 .base = .{
44 .tag = .xcoff,
45 .comp = comp,
46 .emit = emit,
47 .zcu_object_sub_path = emit.sub_path,
48 .gc_sections = options.gc_sections orelse false,
49 .print_gc_sections = options.print_gc_sections,
50 .stack_size = options.stack_size orelse 0,
51 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
52 .file = null,
53 .disable_lld_caching = options.disable_lld_caching,
54 .build_id = options.build_id,
55 },
56 .llvm_object = llvm_object,
57 };
58
59 return xcoff;
60}
61
62pub fn open(
63 arena: Allocator,
64 comp: *Compilation,
65 emit: Path,
66 options: link.File.OpenOptions,
67) !*Xcoff {
68 const target = comp.root_mod.resolved_target.result;
69 assert(target.ofmt == .xcoff);
70 return createEmpty(arena, comp, emit, options);
71}
72
73pub fn deinit(self: *Xcoff) void {
74 self.llvm_object.deinit();
75}
76
77pub fn updateFunc(
78 self: *Xcoff,
79 pt: Zcu.PerThread,
80 func_index: InternPool.Index,
81 air: Air,
82 liveness: Liveness,
83) link.File.UpdateNavError!void {
84 if (build_options.skip_non_native and builtin.object_format != .xcoff)
85 @panic("Attempted to compile for object format that was disabled by build configuration");
86
87 try self.llvm_object.updateFunc(pt, func_index, air, liveness);
88}
89
90pub fn updateNav(self: *Xcoff, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
91 if (build_options.skip_non_native and builtin.object_format != .xcoff)
92 @panic("Attempted to compile for object format that was disabled by build configuration");
93
94 return self.llvm_object.updateNav(pt, nav);
95}
96
97pub fn updateExports(
98 self: *Xcoff,
99 pt: Zcu.PerThread,
100 exported: Zcu.Exported,
101 export_indices: []const Zcu.Export.Index,
102) !void {
103 if (build_options.skip_non_native and builtin.object_format != .xcoff)
104 @panic("Attempted to compile for object format that was disabled by build configuration");
105
106 return self.llvm_object.updateExports(pt, exported, export_indices);
107}
108
109pub fn flush(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
110 return self.flushModule(arena, tid, prog_node);
111}
112
113pub fn flushModule(self: *Xcoff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
114 if (build_options.skip_non_native and builtin.object_format != .xcoff)
115 @panic("Attempted to compile for object format that was disabled by build configuration");
116
117 _ = tid;
118
119 try self.base.emitLlvmObject(arena, self.llvm_object, prog_node);
120}