authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-12-01 04:30:28+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-01-05 16:50:46+01:00
loga78f891d0528244382e902fb0757cce6f1db9c8a
tree62fcef3375977e7cb18aa91926c181868027a026
parent00e6895bde4b5871a944bccf4521b2f6f5f879f6
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

compiler: support building openbsd crt0 and stub shared libraries

closes #2878

4 files changed, 767 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -547,6 +547,7 @@ set(ZIG_STAGE2_SOURCES...@@ -547,6 +547,7 @@ set(ZIG_STAGE2_SOURCES
547 src/libs/freebsd.zig547 src/libs/freebsd.zig
548 src/libs/glibc.zig548 src/libs/glibc.zig
549 src/libs/netbsd.zig549 src/libs/netbsd.zig
550 src/libs/openbsd.zig
550 src/introspect.zig551 src/introspect.zig
551 src/libs/libcxx.zig552 src/libs/libcxx.zig
552 src/libs/libtsan.zig553 src/libs/libtsan.zig
src/Compilation.zig+55
...@@ -27,6 +27,7 @@ const glibc = @import("libs/glibc.zig");...@@ -27,6 +27,7 @@ const glibc = @import("libs/glibc.zig");
27const musl = @import("libs/musl.zig");27const musl = @import("libs/musl.zig");
28const freebsd = @import("libs/freebsd.zig");28const freebsd = @import("libs/freebsd.zig");
29const netbsd = @import("libs/netbsd.zig");29const netbsd = @import("libs/netbsd.zig");
30const openbsd = @import("libs/openbsd.zig");
30const mingw = @import("libs/mingw.zig");31const mingw = @import("libs/mingw.zig");
31const libunwind = @import("libs/libunwind.zig");32const libunwind = @import("libs/libunwind.zig");
32const libcxx = @import("libs/libcxx.zig");33const libcxx = @import("libs/libcxx.zig");
...@@ -243,6 +244,7 @@ fuzzer_lib: ?CrtFile = null,...@@ -243,6 +244,7 @@ fuzzer_lib: ?CrtFile = null,
243glibc_so_files: ?glibc.BuiltSharedObjects = null,244glibc_so_files: ?glibc.BuiltSharedObjects = null,
244freebsd_so_files: ?freebsd.BuiltSharedObjects = null,245freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
245netbsd_so_files: ?netbsd.BuiltSharedObjects = null,246netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
247openbsd_so_files: ?openbsd.BuiltSharedObjects = null,
246248
247/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,249/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
248/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.250/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
...@@ -307,6 +309,7 @@ const QueuedJobs = struct {...@@ -307,6 +309,7 @@ const QueuedJobs = struct {
307 glibc_crt_file: [@typeInfo(glibc.CrtFile).@"enum".fields.len]bool = @splat(false),309 glibc_crt_file: [@typeInfo(glibc.CrtFile).@"enum".fields.len]bool = @splat(false),
308 freebsd_crt_file: [@typeInfo(freebsd.CrtFile).@"enum".fields.len]bool = @splat(false),310 freebsd_crt_file: [@typeInfo(freebsd.CrtFile).@"enum".fields.len]bool = @splat(false),
309 netbsd_crt_file: [@typeInfo(netbsd.CrtFile).@"enum".fields.len]bool = @splat(false),311 netbsd_crt_file: [@typeInfo(netbsd.CrtFile).@"enum".fields.len]bool = @splat(false),
312 openbsd_crt_file: [@typeInfo(openbsd.CrtFile).@"enum".fields.len]bool = @splat(false),
310 /// one of WASI libc static objects313 /// one of WASI libc static objects
311 wasi_libc_crt_file: [@typeInfo(wasi_libc.CrtFile).@"enum".fields.len]bool = @splat(false),314 wasi_libc_crt_file: [@typeInfo(wasi_libc.CrtFile).@"enum".fields.len]bool = @splat(false),
312 /// one of the mingw-w64 static objects315 /// one of the mingw-w64 static objects
...@@ -315,6 +318,7 @@ const QueuedJobs = struct {...@@ -315,6 +318,7 @@ const QueuedJobs = struct {
315 glibc_shared_objects: bool = false,318 glibc_shared_objects: bool = false,
316 freebsd_shared_objects: bool = false,319 freebsd_shared_objects: bool = false,
317 netbsd_shared_objects: bool = false,320 netbsd_shared_objects: bool = false,
321 openbsd_shared_objects: bool = false,
318 /// libunwind.a, usually needed when linking libc322 /// libunwind.a, usually needed when linking libc
319 libunwind: bool = false,323 libunwind: bool = false,
320 libcxx: bool = false,324 libcxx: bool = false,
...@@ -1400,6 +1404,8 @@ pub const MiscTask = enum {...@@ -1400,6 +1404,8 @@ pub const MiscTask = enum {
1400 freebsd_shared_objects,1404 freebsd_shared_objects,
1401 netbsd_crt_file,1405 netbsd_crt_file,
1402 netbsd_shared_objects,1406 netbsd_shared_objects,
1407 openbsd_crt_file,
1408 openbsd_shared_objects,
1403 mingw_crt_file,1409 mingw_crt_file,
1404 windows_import_lib,1410 windows_import_lib,
1405 libunwind,1411 libunwind,
...@@ -1436,6 +1442,9 @@ pub const MiscTask = enum {...@@ -1436,6 +1442,9 @@ pub const MiscTask = enum {
1436 @"netbsd libc Scrt0.o",1442 @"netbsd libc Scrt0.o",
1437 @"netbsd libc shared object",1443 @"netbsd libc shared object",
14381444
1445 @"openbsd libc Scrt0.o",
1446 @"openbsd libc shared object",
1447
1439 @"mingw-w64 crt2.o",1448 @"mingw-w64 crt2.o",
1440 @"mingw-w64 dllcrt2.o",1449 @"mingw-w64 dllcrt2.o",
1441 @"mingw-w64 libmingw32.lib",1450 @"mingw-w64 libmingw32.lib",
...@@ -2620,6 +2629,14 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,...@@ -2620,6 +2629,14 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
2620 }2629 }
26212630
2622 comp.queued_jobs.netbsd_shared_objects = true;2631 comp.queued_jobs.netbsd_shared_objects = true;
2632 } else if (target.isOpenBSDLibC()) {
2633 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
2634
2635 if (openbsd.needsCrt0(comp.config.output_mode)) |f| {
2636 comp.queued_jobs.openbsd_crt_file[@intFromEnum(f)] = true;
2637 }
2638
2639 comp.queued_jobs.openbsd_shared_objects = true;
2623 } else if (target.isWasiLibC()) {2640 } else if (target.isWasiLibC()) {
2624 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);2641 if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable);
26252642
...@@ -2770,6 +2787,10 @@ pub fn destroy(comp: *Compilation) void {...@@ -2770,6 +2787,10 @@ pub fn destroy(comp: *Compilation) void {
2770 netbsd_file.deinit(gpa, io);2787 netbsd_file.deinit(gpa, io);
2771 }2788 }
27722789
2790 if (comp.openbsd_so_files) |*openbsd_file| {
2791 openbsd_file.deinit(gpa, io);
2792 }
2793
2773 for (comp.c_object_table.keys()) |key| {2794 for (comp.c_object_table.keys()) |key| {
2774 key.destroy(gpa, io);2795 key.destroy(gpa, io);
2775 }2796 }
...@@ -4992,6 +5013,10 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node...@@ -4992,6 +5013,10 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node
4992 prelink_group.async(io, buildNetBSDSharedObjects, .{ comp, main_progress_node });5013 prelink_group.async(io, buildNetBSDSharedObjects, .{ comp, main_progress_node });
4993 }5014 }
49945015
5016 if (comp.queued_jobs.openbsd_shared_objects) {
5017 prelink_group.async(io, buildOpenBSDSharedObjects, .{ comp, main_progress_node });
5018 }
5019
4995 if (comp.queued_jobs.libunwind) {5020 if (comp.queued_jobs.libunwind) {
4996 prelink_group.async(io, buildLibUnwind, .{ comp, main_progress_node });5021 prelink_group.async(io, buildLibUnwind, .{ comp, main_progress_node });
4997 }5022 }
...@@ -5040,6 +5065,13 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node...@@ -5040,6 +5065,13 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node
5040 }5065 }
5041 }5066 }
50425067
5068 for (0..@typeInfo(openbsd.CrtFile).@"enum".fields.len) |i| {
5069 if (comp.queued_jobs.openbsd_crt_file[i]) {
5070 const tag: openbsd.CrtFile = @enumFromInt(i);
5071 prelink_group.async(io, buildOpenBSDCrtFile, .{ comp, tag, main_progress_node });
5072 }
5073 }
5074
5043 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {5075 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
5044 if (comp.queued_jobs.wasi_libc_crt_file[i]) {5076 if (comp.queued_jobs.wasi_libc_crt_file[i]) {
5045 const tag: wasi_libc.CrtFile = @enumFromInt(i);5077 const tag: wasi_libc.CrtFile = @enumFromInt(i);
...@@ -6041,6 +6073,29 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo...@@ -6041,6 +6073,29 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo
6041 }6073 }
6042}6074}
60436075
6076fn buildOpenBSDCrtFile(comp: *Compilation, crt_file: openbsd.CrtFile, prog_node: std.Progress.Node) void {
6077 if (openbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
6078 comp.queued_jobs.openbsd_crt_file[@intFromEnum(crt_file)] = false;
6079 } else |err| switch (err) {
6080 error.AlreadyReported => return,
6081 else => comp.lockAndSetMiscFailure(.openbsd_crt_file, "unable to build OpenBSD {s}: {s}", .{
6082 @tagName(crt_file), @errorName(err),
6083 }),
6084 }
6085}
6086
6087fn buildOpenBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
6088 if (openbsd.buildSharedObjects(comp, prog_node)) |_| {
6089 // The job should no longer be queued up since it succeeded.
6090 comp.queued_jobs.openbsd_shared_objects = false;
6091 } else |err| switch (err) {
6092 error.AlreadyReported => return,
6093 else => comp.lockAndSetMiscFailure(.openbsd_shared_objects, "unable to build OpenBSD libc shared objects: {s}", .{
6094 @errorName(err),
6095 }),
6096 }
6097}
6098
6044fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {6099fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
6045 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {6100 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
6046 comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;6101 comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
src/libs/openbsd.zig created+703
...@@ -0,0 +1,703 @@
1const std = @import("std");
2const Io = std.Io;
3const Allocator = std.mem.Allocator;
4const mem = std.mem;
5const log = std.log;
6const fs = std.fs;
7const path = fs.path;
8const assert = std.debug.assert;
9const Version = std.SemanticVersion;
10const Path = std.Build.Cache.Path;
11
12const Compilation = @import("../Compilation.zig");
13const build_options = @import("build_options");
14const trace = @import("../tracy.zig").trace;
15const Cache = std.Build.Cache;
16const Module = @import("../Package/Module.zig");
17const link = @import("../link.zig");
18
19pub const CrtFile = enum {
20 scrt0_o,
21};
22
23pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile {
24 // https://github.com/ziglang/zig/issues/23574#issuecomment-2869089897
25 return switch (output_mode) {
26 .Obj, .Lib => null,
27 .Exe => .scrt0_o,
28 };
29}
30
31fn includePath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
32 return path.join(arena, &.{
33 comp.dirs.zig_lib.path.?,
34 "libc" ++ path.sep_str ++ "include",
35 sub_path,
36 });
37}
38
39fn csuPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
40 return path.join(arena, &.{
41 comp.dirs.zig_lib.path.?,
42 "libc" ++ path.sep_str ++ "openbsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "csu",
43 sub_path,
44 });
45}
46
47/// TODO replace anyerror with explicit error set, recording user-friendly errors with
48/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
49pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
50 if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
51
52 const gpa = comp.gpa;
53 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
54 defer arena_allocator.deinit();
55 const arena = arena_allocator.allocator();
56
57 const target = &comp.root_mod.resolved_target.result;
58 const target_version = target.os.version_range.semver.min;
59
60 // In all cases in this function, we add the C compiler flags to
61 // cache_exempt_flags rather than extra_flags, because these arguments
62 // depend on only properties that are already covered by the cache
63 // manifest. Including these arguments in the cache could only possibly
64 // waste computation and create false negatives.
65
66 switch (crt_file) {
67 .scrt0_o => {
68 var cflags = std.array_list.Managed([]const u8).init(arena);
69 try cflags.appendSlice(&.{
70 "-w", // Disable all warnings.
71 });
72
73 // See `Compilation.addCommonCCArgs`.
74 try cflags.append(try std.fmt.allocPrint(arena, "-D___OpenBSD={d}", .{
75 202510,
76 }));
77 try cflags.append(try std.fmt.allocPrint(arena, "-DOpenBSD{d}_{d}", .{
78 target_version.major,
79 target_version.minor,
80 }));
81
82 try cflags.appendSlice(&.{
83 "-I",
84 try includePath(comp, arena, try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{
85 std.zig.target.openbsdArchNameHeaders(target.cpu.arch),
86 @tagName(target.os.tag),
87 @tagName(target.abi),
88 })),
89 "-I",
90 try includePath(comp, arena, "generic-openbsd"),
91 "-I",
92 try csuPath(comp, arena, switch (target.cpu.arch) {
93 .mips64el => "mips64",
94 .x86 => "i386",
95 .x86_64 => "amd64",
96 else => |t| @tagName(t),
97 }),
98 "-Qunused-arguments",
99 });
100
101 const sources = [_]struct {
102 path: []const u8,
103 flags: []const []const u8,
104 }{
105 .{
106 .path = "crt0.c",
107 .flags = cflags.items,
108 },
109 .{
110 .path = "crtbegin.c",
111 .flags = cflags.items,
112 },
113 };
114
115 var files_buf: [sources.len]Compilation.CSourceFile = undefined;
116 var files_index: usize = 0;
117 for (sources) |file| {
118 files_buf[files_index] = .{
119 .src_path = try csuPath(comp, arena, file.path),
120 .cache_exempt_flags = file.flags,
121 .owner = undefined,
122 };
123 files_index += 1;
124 }
125 const files = files_buf[0..files_index];
126
127 return comp.build_crt_file("crt0", .Obj, .@"openbsd libc Scrt0.o", prog_node, files, .{
128 // Unclear why OpenBSD does this, but we'll do the same.
129 .omit_frame_pointer = if (target.cpu.arch.isX86()) false else null,
130 .pic = true,
131 });
132 },
133 }
134}
135
136pub const Lib = struct {
137 name: []const u8,
138};
139
140// Library versions are bumped frequently on OpenBSD. Fortunately, by linking to
141// just libc.so, the dynamic linker will happily bind to e.g. libc.so.102.0.
142pub const libs = [_]Lib{
143 .{ .name = "m" },
144 .{ .name = "pthread" },
145 .{ .name = "c" },
146 .{ .name = "ld" },
147 .{ .name = "util" },
148 .{ .name = "execinfo" },
149};
150
151pub const ABI = struct {
152 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
153 all_targets: []const std.zig.target.ArchOsAbi,
154 /// The bytes from the file verbatim, starting from the u16 number
155 /// of function inclusions.
156 inclusions: []const u8,
157 arena_state: std.heap.ArenaAllocator.State,
158
159 pub fn destroy(abi: *ABI, gpa: Allocator) void {
160 abi.arena_state.promote(gpa).deinit();
161 }
162};
163
164pub const LoadMetaDataError = error{
165 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
166 ZigInstallationCorrupt,
167 OutOfMemory,
168};
169
170pub const abilists_path = "libc" ++ path.sep_str ++ "openbsd" ++ path.sep_str ++ "abilists";
171pub const abilists_max_size = 300 * 1024; // Bigger than this and something is definitely borked.
172
173/// This function will emit a log error when there is a problem with the zig
174/// installation and then return `error.ZigInstallationCorrupt`.
175pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
176 const tracy = trace(@src());
177 defer tracy.end();
178
179 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
180 errdefer arena_allocator.deinit();
181 const arena = arena_allocator.allocator();
182
183 var index: usize = 0;
184
185 {
186 const libs_len = contents[index];
187 index += 1;
188
189 var i: u8 = 0;
190 while (i < libs_len) : (i += 1) {
191 const lib_name = mem.sliceTo(contents[index..], 0);
192 index += lib_name.len + 1;
193
194 if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
195 log.err("libc" ++ path.sep_str ++ "openbsd" ++ path.sep_str ++
196 "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
197 return error.ZigInstallationCorrupt;
198 }
199 }
200 }
201
202 const versions = b: {
203 const versions_len = contents[index];
204 index += 1;
205
206 const versions = try arena.alloc(Version, versions_len);
207 var i: u8 = 0;
208 while (i < versions.len) : (i += 1) {
209 versions[i] = .{
210 .major = contents[index + 0],
211 .minor = contents[index + 1],
212 .patch = contents[index + 2],
213 };
214 index += 3;
215 }
216 break :b versions;
217 };
218
219 const targets = b: {
220 const targets_len = contents[index];
221 index += 1;
222
223 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
224 var i: u8 = 0;
225 while (i < targets.len) : (i += 1) {
226 const target_name = mem.sliceTo(contents[index..], 0);
227 index += target_name.len + 1;
228
229 var component_it = mem.tokenizeScalar(u8, target_name, '-');
230 const arch_name = component_it.next() orelse {
231 log.err("abilists: expected arch name", .{});
232 return error.ZigInstallationCorrupt;
233 };
234 const os_name = component_it.next() orelse {
235 log.err("abilists: expected OS name", .{});
236 return error.ZigInstallationCorrupt;
237 };
238 const abi_name = component_it.next() orelse {
239 log.err("abilists: expected ABI name", .{});
240 return error.ZigInstallationCorrupt;
241 };
242 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
243 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
244 return error.ZigInstallationCorrupt;
245 };
246 if (!mem.eql(u8, os_name, "openbsd")) {
247 log.err("abilists: expected OS 'openbsd', found '{s}'", .{os_name});
248 return error.ZigInstallationCorrupt;
249 }
250 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
251 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
252 return error.ZigInstallationCorrupt;
253 };
254
255 targets[i] = .{
256 .arch = arch_tag,
257 .os = .openbsd,
258 .abi = abi_tag,
259 };
260 }
261 break :b targets;
262 };
263
264 const abi = try arena.create(ABI);
265 abi.* = .{
266 .all_versions = versions,
267 .all_targets = targets,
268 .inclusions = contents[index..],
269 .arena_state = arena_allocator.state,
270 };
271 return abi;
272}
273
274pub const BuiltSharedObjects = struct {
275 lock: Cache.Lock,
276 dir_path: Path,
277
278 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator, io: Io) void {
279 self.lock.release(io);
280 gpa.free(self.dir_path.sub_path);
281 self.* = undefined;
282 }
283};
284
285fn wordDirective(target: *const std.Target) []const u8 {
286 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
287 // according to the target word size. But no; that would just make too much sense.
288 return if (target.ptrBitWidth() == 64) ".quad" else ".long";
289}
290
291/// TODO replace anyerror with explicit error set, recording user-friendly errors with
292/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
293pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
294 // See also glibc.zig which this code is based on.
295
296 const tracy = trace(@src());
297 defer tracy.end();
298
299 if (!build_options.have_llvm) {
300 return error.ZigCompilerNotBuiltWithLLVMExtensions;
301 }
302
303 const gpa = comp.gpa;
304 const io = comp.io;
305
306 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
307 defer arena_allocator.deinit();
308 const arena = arena_allocator.allocator();
309
310 const target = comp.getTarget();
311 const target_version = target.os.version_range.semver.min;
312
313 // Use the global cache directory.
314 var cache: Cache = .{
315 .gpa = gpa,
316 .io = io,
317 .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
318 .cwd = comp.dirs.cwd,
319 };
320 cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
321 cache.addPrefix(comp.dirs.zig_lib);
322 cache.addPrefix(comp.dirs.global_cache);
323 defer cache.manifest_dir.close(io);
324
325 var man = cache.obtain();
326 defer man.deinit();
327 man.hash.addBytes(build_options.version);
328 man.hash.add(target.cpu.arch);
329 man.hash.add(target.abi);
330 man.hash.add(target_version);
331
332 const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path});
333 const abilists_index = try man.addFile(full_abilists_path, abilists_max_size);
334
335 if (try man.hit()) {
336 const digest = man.final();
337
338 return queueSharedObjects(comp, .{
339 .lock = man.toOwnedLock(),
340 .dir_path = .{
341 .root_dir = comp.dirs.global_cache,
342 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
343 },
344 });
345 }
346
347 const digest = man.final();
348 const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
349
350 var o_directory: Cache.Directory = .{
351 .handle = try comp.dirs.global_cache.handle.createDirPathOpen(io, o_sub_path, .{}),
352 .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}),
353 };
354 defer o_directory.handle.close(io);
355
356 const abilists_contents = man.files.keys()[abilists_index].contents.?;
357 const metadata = try loadMetaData(gpa, abilists_contents);
358 defer metadata.destroy(gpa);
359
360 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
361 if (targ.arch == target.cpu.arch and
362 targ.os == target.os.tag and
363 targ.abi == target.abi)
364 {
365 break i;
366 }
367 } else {
368 unreachable; // std.zig.target.available_libcs prevents us from getting here
369 };
370
371 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
372 switch (ver.order(target_version)) {
373 .eq => break i,
374 .lt => continue,
375 .gt => {
376 // TODO Expose via compile error mechanism instead of log.
377 log.warn("invalid target OpenBSD libc version: {f}", .{target_version});
378 return error.InvalidTargetLibCVersion;
379 },
380 }
381 } else blk: {
382 const latest_index = metadata.all_versions.len - 1;
383 log.warn("zig cannot build new OpenBSD libc version {f}; providing instead {f}", .{
384 target_version, metadata.all_versions[latest_index],
385 });
386 break :blk latest_index;
387 };
388
389 var stubs_asm = std.array_list.Managed(u8).init(gpa);
390 defer stubs_asm.deinit();
391
392 for (libs, 0..) |lib, lib_i| {
393 stubs_asm.shrinkRetainingCapacity(0);
394
395 try stubs_asm.appendSlice(".text\n");
396
397 var sym_i: usize = 0;
398 var sym_name_buf: std.Io.Writer.Allocating = .init(arena);
399 var opt_symbol_name: ?[]const u8 = null;
400
401 var inc_reader: std.Io.Reader = .fixed(metadata.inclusions);
402
403 const fn_inclusions_len = try inc_reader.takeInt(u16, .little);
404
405 var chosen_ver_index: usize = 255;
406 var chosen_is_weak: bool = undefined;
407
408 while (sym_i < fn_inclusions_len) : (sym_i += 1) {
409 const sym_name = opt_symbol_name orelse n: {
410 sym_name_buf.clearRetainingCapacity();
411 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
412 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
413 inc_reader.toss(1);
414
415 opt_symbol_name = sym_name_buf.written();
416 chosen_ver_index = 255;
417
418 break :n sym_name_buf.written();
419 };
420
421 {
422 const targets = try inc_reader.takeLeb128(u64);
423 var lib_index = try inc_reader.takeByte();
424
425 const is_weak = (lib_index & (1 << 6)) != 0;
426 const is_terminal = (lib_index & (1 << 7)) != 0;
427
428 lib_index = @as(u5, @truncate(lib_index));
429
430 // Test whether the inclusion applies to our current library and target.
431 const ok_lib_and_target =
432 (lib_index == lib_i) and
433 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
434
435 while (true) {
436 const byte = try inc_reader.takeByte();
437 const last = (byte & 0b1000_0000) != 0;
438 const ver_i = @as(u7, @truncate(byte));
439 if (ok_lib_and_target and ver_i <= target_ver_index and
440 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
441 {
442 chosen_ver_index = ver_i;
443 chosen_is_weak = is_weak;
444 }
445 if (last) break;
446 }
447
448 if (is_terminal) {
449 opt_symbol_name = null;
450 } else continue;
451 }
452
453 if (chosen_ver_index != 255) {
454 // Example:
455 // .balign 4
456 // .globl _Exit
457 // .type _Exit, %function
458 // _Exit: .long 0
459 try stubs_asm.print(
460 \\.balign {d}
461 \\.{s} {s}
462 \\.type {s}, %function
463 \\{s}: {s} 0
464 \\
465 , .{
466 target.ptrBitWidth() / 8,
467 if (chosen_is_weak) "weak" else "globl",
468 sym_name,
469 sym_name,
470 sym_name,
471 wordDirective(target),
472 });
473 }
474 }
475
476 try stubs_asm.appendSlice(".data\n");
477
478 const obj_inclusions_len = try inc_reader.takeInt(u16, .little);
479
480 sym_i = 0;
481 opt_symbol_name = null;
482
483 var chosen_size: u16 = undefined;
484
485 while (sym_i < obj_inclusions_len) : (sym_i += 1) {
486 const sym_name = opt_symbol_name orelse n: {
487 sym_name_buf.clearRetainingCapacity();
488 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
489 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
490 inc_reader.toss(1);
491
492 opt_symbol_name = sym_name_buf.written();
493 chosen_ver_index = 255;
494
495 break :n sym_name_buf.written();
496 };
497
498 {
499 const targets = try inc_reader.takeLeb128(u64);
500 const size = try inc_reader.takeLeb128(u16);
501 var lib_index = try inc_reader.takeByte();
502
503 const is_weak = (lib_index & (1 << 6)) != 0;
504 const is_terminal = (lib_index & (1 << 7)) != 0;
505
506 lib_index = @as(u5, @truncate(lib_index));
507
508 // Test whether the inclusion applies to our current library and target.
509 const ok_lib_and_target =
510 (lib_index == lib_i) and
511 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
512
513 while (true) {
514 const byte = try inc_reader.takeByte();
515 const last = (byte & 0b1000_0000) != 0;
516 const ver_i = @as(u7, @truncate(byte));
517 if (ok_lib_and_target and ver_i <= target_ver_index and
518 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
519 {
520 chosen_ver_index = ver_i;
521 chosen_size = size;
522 chosen_is_weak = is_weak;
523 }
524 if (last) break;
525 }
526
527 if (is_terminal) {
528 opt_symbol_name = null;
529 } else continue;
530 }
531
532 if (chosen_ver_index != 255) {
533 // Example:
534 // .balign 4
535 // .globl malloc_conf
536 // .type malloc_conf, %object
537 // .size malloc_conf, 4
538 // malloc_conf: .fill 4, 1, 0
539 try stubs_asm.print(
540 \\.balign {d}
541 \\.{s} {s}
542 \\.type {s}, %object
543 \\.size {s}, {d}
544 \\{s}: {s} 0
545 \\
546 , .{
547 target.ptrBitWidth() / 8,
548 if (chosen_is_weak) "weak" else "globl",
549 sym_name,
550 sym_name,
551 sym_name,
552 chosen_size,
553 sym_name,
554 wordDirective(target),
555 });
556 }
557 }
558
559 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
560 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
561 try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items });
562 try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node);
563 }
564
565 man.writeManifest() catch |err| {
566 log.warn("failed to write cache manifest for OpenBSD libc stubs: {s}", .{@errorName(err)});
567 };
568
569 return queueSharedObjects(comp, .{
570 .lock = man.toOwnedLock(),
571 .dir_path = .{
572 .root_dir = comp.dirs.global_cache,
573 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
574 },
575 });
576}
577
578fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
579 const io = comp.io;
580 assert(comp.openbsd_so_files == null);
581 comp.openbsd_so_files = so_files;
582
583 var task_buffer: [libs.len]link.PrelinkTask = undefined;
584 var task_buffer_i: usize = 0;
585
586 {
587 comp.mutex.lockUncancelable(io); // protect comp.arena
588 defer comp.mutex.unlock(io);
589
590 for (libs) |lib| {
591 const so_path: Path = .{
592 .root_dir = so_files.dir_path.root_dir,
593 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so", .{
594 so_files.dir_path.sub_path, path.sep, lib.name,
595 }) catch return comp.setAllocFailure(),
596 };
597 task_buffer[task_buffer_i] = .{ .load_dso = so_path };
598 task_buffer_i += 1;
599 }
600 }
601
602 try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
603}
604
605fn buildSharedLib(
606 comp: *Compilation,
607 arena: Allocator,
608 bin_directory: Cache.Directory,
609 asm_file_basename: []const u8,
610 lib: Lib,
611 prog_node: std.Progress.Node,
612) !void {
613 const tracy = trace(@src());
614 defer tracy.end();
615
616 const io = comp.io;
617 const basename = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib.name});
618 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
619 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
620
621 const optimize_mode = comp.compilerRtOptMode();
622 const strip = comp.compilerRtStrip();
623 const config = try Compilation.Config.resolve(.{
624 .output_mode = .Lib,
625 .link_mode = .dynamic,
626 .resolved_target = comp.root_mod.resolved_target,
627 .is_test = false,
628 .have_zcu = false,
629 .emit_bin = true,
630 .root_optimize_mode = optimize_mode,
631 .root_strip = strip,
632 .link_libc = false,
633 });
634
635 const root_mod = try Module.create(arena, .{
636 .paths = .{
637 .root = .zig_lib_root,
638 .root_src_path = "",
639 },
640 .fully_qualified_name = "root",
641 .inherited = .{
642 .resolved_target = comp.root_mod.resolved_target,
643 .strip = strip,
644 .stack_check = false,
645 .stack_protector = 0,
646 .sanitize_c = .off,
647 .sanitize_thread = false,
648 .red_zone = comp.root_mod.red_zone,
649 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
650 .valgrind = false,
651 .optimize_mode = optimize_mode,
652 .structured_cfg = comp.root_mod.structured_cfg,
653 },
654 .global = config,
655 .cc_argv = &.{},
656 .parent = null,
657 });
658
659 const c_source_files = [1]Compilation.CSourceFile{
660 .{
661 .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
662 .owner = root_mod,
663 },
664 };
665
666 const misc_task: Compilation.MiscTask = .@"openbsd libc shared object";
667
668 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
669 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
670 .dirs = comp.dirs.withoutLocalCache(),
671 .thread_limit = comp.thread_limit,
672 .self_exe_path = comp.self_exe_path,
673 // Because we manually cache the whole set of objects, we don't cache the individual objects
674 // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path.
675 .cache_mode = .none,
676 .config = config,
677 .root_mod = root_mod,
678 .root_name = lib.name,
679 .libc_installation = comp.libc_installation,
680 .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) },
681 .verbose_cc = comp.verbose_cc,
682 .verbose_link = comp.verbose_link,
683 .verbose_air = comp.verbose_air,
684 .verbose_llvm_ir = comp.verbose_llvm_ir,
685 .verbose_llvm_bc = comp.verbose_llvm_bc,
686 .verbose_cimport = comp.verbose_cimport,
687 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
688 .clang_passthrough_mode = comp.clang_passthrough_mode,
689 .soname = soname,
690 .c_source_files = &c_source_files,
691 .skip_linker_dependencies = true,
692 .environ_map = comp.environ_map,
693 }) catch |err| switch (err) {
694 error.CreateFail => {
695 comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
696 return error.AlreadyReported;
697 },
698 else => |e| return e,
699 };
700 defer sub_compilation.destroy();
701
702 try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
703}
src/link/Lld.zig+8
...@@ -1210,6 +1210,13 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1210,6 +1210,13 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1210 });1210 });
1211 try argv.append(lib_path);1211 try argv.append(lib_path);
1212 }1212 }
1213 } else if (target.isOpenBSDLibC()) {
1214 for (openbsd.libs) |lib| {
1215 const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so", .{
1216 comp.openbsd_so_files.?.dir_path, fs.path.sep, lib.name,
1217 });
1218 try argv.append(lib_path);
1219 }
1213 } else {1220 } else {
1214 diags.flags.missing_libc = true;1221 diags.flags.missing_libc = true;
1215 }1222 }
...@@ -1713,6 +1720,7 @@ const dev = @import("../dev.zig");...@@ -1713,6 +1720,7 @@ const dev = @import("../dev.zig");
1713const freebsd = @import("../libs/freebsd.zig");1720const freebsd = @import("../libs/freebsd.zig");
1714const glibc = @import("../libs/glibc.zig");1721const glibc = @import("../libs/glibc.zig");
1715const netbsd = @import("../libs/netbsd.zig");1722const netbsd = @import("../libs/netbsd.zig");
1723const openbsd = @import("../libs/openbsd.zig");
1716const wasi_libc = @import("../libs/wasi_libc.zig");1724const wasi_libc = @import("../libs/wasi_libc.zig");
1717const link = @import("../link.zig");1725const link = @import("../link.zig");
1718const lldMain = @import("../main.zig").lldMain;1726const lldMain = @import("../main.zig").lldMain;