authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-05-13 01:49:45+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-05-17 20:12:56+02:00
loga97e417ab1e555d14e6d76df722de92cca85ba2f
tree240a553938a0fdebf5984b6f4cdcc498464abcef
parentc5e669ff768857df3cfab774318938ddfc254b90
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

compiler: Support building NetBSD crt1.o/Scrt1.o and stub shared libraries.

Only works for NetBSD 10.1+. Note that we still default to targeting NetBSD 9. Contributes to #2877.

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

CMakeLists.txt+1
......@@ -588,6 +588,7 @@ set(ZIG_STAGE2_SOURCES
588588 src/dev.zig
589589 src/libs/freebsd.zig
590590 src/libs/glibc.zig
591 src/libs/netbsd.zig
591592 src/introspect.zig
592593 src/libs/libcxx.zig
593594 src/libs/libtsan.zig
src/Compilation.zig+57
......@@ -24,6 +24,7 @@ const LibCInstallation = std.zig.LibCInstallation;
2424const glibc = @import("libs/glibc.zig");
2525const musl = @import("libs/musl.zig");
2626const freebsd = @import("libs/freebsd.zig");
27const netbsd = @import("libs/netbsd.zig");
2728const mingw = @import("libs/mingw.zig");
2829const libunwind = @import("libs/libunwind.zig");
2930const libcxx = @import("libs/libcxx.zig");
......@@ -250,6 +251,7 @@ fuzzer_lib: ?CrtFile = null,
250251
251252glibc_so_files: ?glibc.BuiltSharedObjects = null,
252253freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
254netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
253255wasi_emulated_libs: []const wasi_libc.CrtFile,
254256
255257/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
......@@ -297,6 +299,7 @@ const QueuedJobs = struct {
297299 musl_crt_file: [@typeInfo(musl.CrtFile).@"enum".fields.len]bool = @splat(false),
298300 glibc_crt_file: [@typeInfo(glibc.CrtFile).@"enum".fields.len]bool = @splat(false),
299301 freebsd_crt_file: [@typeInfo(freebsd.CrtFile).@"enum".fields.len]bool = @splat(false),
302 netbsd_crt_file: [@typeInfo(netbsd.CrtFile).@"enum".fields.len]bool = @splat(false),
300303 /// one of WASI libc static objects
301304 wasi_libc_crt_file: [@typeInfo(wasi_libc.CrtFile).@"enum".fields.len]bool = @splat(false),
302305 /// one of the mingw-w64 static objects
......@@ -304,6 +307,7 @@ const QueuedJobs = struct {
304307 /// all of the glibc shared objects
305308 glibc_shared_objects: bool = false,
306309 freebsd_shared_objects: bool = false,
310 netbsd_shared_objects: bool = false,
307311 /// libunwind.a, usually needed when linking libc
308312 libunwind: bool = false,
309313 libcxx: bool = false,
......@@ -795,6 +799,8 @@ pub const MiscTask = enum {
795799 musl_crt_file,
796800 freebsd_crt_file,
797801 freebsd_shared_objects,
802 netbsd_crt_file,
803 netbsd_shared_objects,
798804 mingw_crt_file,
799805 windows_import_lib,
800806 libunwind,
......@@ -832,6 +838,9 @@ pub const MiscTask = enum {
832838 @"freebsd libc Scrt1.o",
833839 @"freebsd libc shared object",
834840
841 @"netbsd libc Scrt0.o",
842 @"netbsd libc shared object",
843
835844 @"mingw-w64 crt2.o",
836845 @"mingw-w64 dllcrt2.o",
837846 @"mingw-w64 libmingw32.lib",
......@@ -1893,6 +1902,16 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
18931902
18941903 comp.queued_jobs.freebsd_shared_objects = true;
18951904 comp.remaining_prelink_tasks += freebsd.sharedObjectsCount();
1905 } else if (target.isNetBSDLibC()) {
1906 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
1907
1908 if (netbsd.needsCrt0(comp.config.output_mode)) |f| {
1909 comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true;
1910 comp.remaining_prelink_tasks += 1;
1911 }
1912
1913 comp.queued_jobs.netbsd_shared_objects = true;
1914 comp.remaining_prelink_tasks += netbsd.sharedObjectsCount();
18961915 } else if (target.isWasiLibC()) {
18971916 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
18981917
......@@ -2051,6 +2070,10 @@ pub fn destroy(comp: *Compilation) void {
20512070 freebsd_file.deinit(gpa);
20522071 }
20532072
2073 if (comp.netbsd_so_files) |*netbsd_file| {
2074 netbsd_file.deinit(gpa);
2075 }
2076
20542077 for (comp.c_object_table.keys()) |key| {
20552078 key.destroy(gpa);
20562079 }
......@@ -3866,6 +3889,10 @@ fn performAllTheWorkInner(
38663889 comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node });
38673890 }
38683891
3892 if (comp.queued_jobs.netbsd_shared_objects) {
3893 comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node });
3894 }
3895
38693896 if (comp.queued_jobs.libunwind) {
38703897 comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node });
38713898 }
......@@ -3907,6 +3934,13 @@ fn performAllTheWorkInner(
39073934 }
39083935 }
39093936
3937 for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
3938 if (comp.queued_jobs.netbsd_crt_file[i]) {
3939 const tag: netbsd.CrtFile = @enumFromInt(i);
3940 comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
3941 }
3942 }
3943
39103944 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
39113945 if (comp.queued_jobs.wasi_libc_crt_file[i]) {
39123946 const tag: wasi_libc.CrtFile = @enumFromInt(i);
......@@ -4933,6 +4967,29 @@ fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) v
49334967 }
49344968}
49354969
4970fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
4971 if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
4972 comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false;
4973 } else |err| switch (err) {
4974 error.SubCompilationFailed => return, // error reported already
4975 else => comp.lockAndSetMiscFailure(.netbsd_crt_file, "unable to build NetBSD {s}: {s}", .{
4976 @tagName(crt_file), @errorName(err),
4977 }),
4978 }
4979}
4980
4981fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
4982 if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
4983 // The job should no longer be queued up since it succeeded.
4984 comp.queued_jobs.netbsd_shared_objects = false;
4985 } else |err| switch (err) {
4986 error.SubCompilationFailed => return, // error reported already
4987 else => comp.lockAndSetMiscFailure(.netbsd_shared_objects, "unable to build NetBSD libc shared objects: {s}", .{
4988 @errorName(err),
4989 }),
4990 }
4991}
4992
49364993fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
49374994 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
49384995 comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
src/libs/netbsd.zig created+773
......@@ -0,0 +1,773 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const path = fs.path;
7const assert = std.debug.assert;
8const Version = std.SemanticVersion;
9const Path = std.Build.Cache.Path;
10
11const Compilation = @import("../Compilation.zig");
12const build_options = @import("build_options");
13const trace = @import("../tracy.zig").trace;
14const Cache = std.Build.Cache;
15const Module = @import("../Package/Module.zig");
16const link = @import("../link.zig");
17
18pub const CrtFile = enum {
19 scrt0_o,
20};
21
22pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile {
23 // For shared libraries and PIC executables, we should actually link in a variant of crt1 that
24 // is built with `-DSHARED` so that it calls `__cxa_finalize` in an ELF destructor. However, we
25 // currently make no effort to respect `__cxa_finalize` on any other targets, so for now, we're
26 // not doing it here either.
27 //
28 // See: https://github.com/ziglang/zig/issues/23574#issuecomment-2869089897
29 return switch (output_mode) {
30 .Obj, .Lib => null,
31 .Exe => .scrt0_o,
32 };
33}
34
35fn includePath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
36 return path.join(arena, &.{
37 comp.zig_lib_directory.path.?,
38 "libc" ++ path.sep_str ++ "include",
39 sub_path,
40 });
41}
42
43fn csuPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
44 return path.join(arena, &.{
45 comp.zig_lib_directory.path.?,
46 "libc" ++ path.sep_str ++ "netbsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "csu",
47 sub_path,
48 });
49}
50
51/// TODO replace anyerror with explicit error set, recording user-friendly errors with
52/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
53pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
54 if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
55
56 const gpa = comp.gpa;
57 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
58 defer arena_allocator.deinit();
59 const arena = arena_allocator.allocator();
60
61 const target = comp.root_mod.resolved_target.result;
62 const target_version = target.os.version_range.semver.min;
63
64 // In all cases in this function, we add the C compiler flags to
65 // cache_exempt_flags rather than extra_flags, because these arguments
66 // depend on only properties that are already covered by the cache
67 // manifest. Including these arguments in the cache could only possibly
68 // waste computation and create false negatives.
69
70 switch (crt_file) {
71 .scrt0_o => {
72 var cflags = std.ArrayList([]const u8).init(arena);
73 try cflags.appendSlice(&.{
74 "-DHAVE_INITFINI_ARRAY",
75 "-w", // Disable all warnings.
76 });
77
78 var acflags = std.ArrayList([]const u8).init(arena);
79 try acflags.appendSlice(&.{
80 // See `Compilation.addCCArgs`.
81 try std.fmt.allocPrint(arena, "-D__NetBSD_Version__={d}", .{(target_version.major * 100_000_000) + (target_version.minor * 1_000_000)}),
82 });
83
84 const csu_march: ?[]const u8 = switch (target.cpu.arch) {
85 .arm => if (target.abi.float() == .hard) "earmhf" else "earm",
86 .armeb => if (target.abi.float() == .hard) "earmhfeb" else "earmeb",
87 else => null,
88 };
89
90 inline for (.{ &cflags, &acflags }) |flags| {
91 if (csu_march) |march| {
92 try flags.appendSlice(&.{
93 try std.fmt.allocPrint(arena, "-DELF_NOTE_MARCH_DESC=\"{s}\"", .{march}),
94 try std.fmt.allocPrint(arena, "-DELF_NOTE_MARCH_DESCSZ={d}", .{march.len + 1}),
95 });
96 }
97
98 try flags.appendSlice(&.{
99 "-I",
100 try includePath(comp, arena, try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{
101 std.zig.target.netbsdArchNameHeaders(target.cpu.arch),
102 @tagName(target.os.tag),
103 @tagName(target.abi),
104 })),
105 "-I",
106 try includePath(comp, arena, "generic-netbsd"),
107 "-I",
108 try csuPath(comp, arena, "common"),
109 "-Qunused-arguments",
110 });
111 }
112
113 const sources = [_]struct {
114 path: []const u8,
115 flags: []const []const u8,
116 condition: bool = true,
117 }{
118 .{
119 .path = "common" ++ path.sep_str ++ "crt0-common.c",
120 .flags = cflags.items,
121 },
122 .{
123 .path = "common" ++ path.sep_str ++ "crtbegin.c",
124 .flags = cflags.items,
125 },
126 .{
127 .path = "common" ++ path.sep_str ++ "sysident.S",
128 .flags = acflags.items,
129 },
130
131 .{
132 .path = "arch" ++ path.sep_str ++ "earm" ++ path.sep_str ++ "crt0.S",
133 .flags = acflags.items,
134 .condition = target.cpu.arch.isArm(),
135 },
136
137 .{
138 .path = "arch" ++ path.sep_str ++ "aarch64" ++ path.sep_str ++ "crt0.S",
139 .flags = acflags.items,
140 .condition = target.cpu.arch.isAARCH64(),
141 },
142
143 .{
144 .path = "arch" ++ path.sep_str ++ "m68k" ++ path.sep_str ++ "crt0.S",
145 .flags = acflags.items,
146 .condition = target.cpu.arch == .m68k,
147 },
148
149 .{
150 .path = "arch" ++ path.sep_str ++ "mips" ++ path.sep_str ++ "crt0.S",
151 .flags = acflags.items,
152 .condition = target.cpu.arch.isMIPS(),
153 },
154
155 .{
156 .path = "arch" ++ path.sep_str ++ "powerpc" ++ path.sep_str ++ "crt0.S",
157 .flags = acflags.items,
158 .condition = target.cpu.arch == .powerpc,
159 },
160
161 .{
162 .path = "arch" ++ path.sep_str ++ "sparc" ++ path.sep_str ++ "crt0.S",
163 .flags = acflags.items,
164 .condition = target.cpu.arch == .sparc,
165 },
166
167 .{
168 .path = "arch" ++ path.sep_str ++ "sparc64" ++ path.sep_str ++ "crt0.S",
169 .flags = acflags.items,
170 .condition = target.cpu.arch == .sparc64,
171 },
172
173 .{
174 .path = "arch" ++ path.sep_str ++ "i386" ++ path.sep_str ++ "crt0.S",
175 .flags = acflags.items,
176 .condition = target.cpu.arch == .x86,
177 },
178
179 .{
180 .path = "arch" ++ path.sep_str ++ "x86_64" ++ path.sep_str ++ "crt0.S",
181 .flags = acflags.items,
182 .condition = target.cpu.arch == .x86_64,
183 },
184 };
185
186 var files_buf: [sources.len]Compilation.CSourceFile = undefined;
187 var files_index: usize = 0;
188 for (sources) |file| {
189 if (!file.condition) continue;
190
191 files_buf[files_index] = .{
192 .src_path = try csuPath(comp, arena, file.path),
193 .cache_exempt_flags = file.flags,
194 .owner = undefined,
195 };
196 files_index += 1;
197 }
198 const files = files_buf[0..files_index];
199
200 return comp.build_crt_file("crt0", .Obj, .@"netbsd libc Scrt0.o", prog_node, files, .{
201 .pic = true,
202 });
203 },
204 }
205}
206
207pub const Lib = struct {
208 name: []const u8,
209 sover: u8,
210};
211
212pub const libs = [_]Lib{
213 .{ .name = "m", .sover = 0 },
214 .{ .name = "pthread", .sover = 1 },
215 .{ .name = "c", .sover = 12 },
216 .{ .name = "rt", .sover = 1 },
217 .{ .name = "ld", .sover = 0 },
218 .{ .name = "util", .sover = 7 },
219 .{ .name = "execinfo", .sover = 0 },
220};
221
222pub const ABI = struct {
223 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
224 all_targets: []const std.zig.target.ArchOsAbi,
225 /// The bytes from the file verbatim, starting from the u16 number
226 /// of function inclusions.
227 inclusions: []const u8,
228 arena_state: std.heap.ArenaAllocator.State,
229
230 pub fn destroy(abi: *ABI, gpa: Allocator) void {
231 abi.arena_state.promote(gpa).deinit();
232 }
233};
234
235pub const LoadMetaDataError = error{
236 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
237 ZigInstallationCorrupt,
238 OutOfMemory,
239};
240
241pub const abilists_path = "libc" ++ path.sep_str ++ "netbsd" ++ path.sep_str ++ "abilists";
242pub const abilists_max_size = 300 * 1024; // Bigger than this and something is definitely borked.
243
244/// This function will emit a log error when there is a problem with the zig
245/// installation and then return `error.ZigInstallationCorrupt`.
246pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
247 const tracy = trace(@src());
248 defer tracy.end();
249
250 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
251 errdefer arena_allocator.deinit();
252 const arena = arena_allocator.allocator();
253
254 var index: usize = 0;
255
256 {
257 const libs_len = contents[index];
258 index += 1;
259
260 var i: u8 = 0;
261 while (i < libs_len) : (i += 1) {
262 const lib_name = mem.sliceTo(contents[index..], 0);
263 index += lib_name.len + 1;
264
265 if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
266 log.err("libc" ++ path.sep_str ++ "netbsd" ++ path.sep_str ++
267 "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
268 return error.ZigInstallationCorrupt;
269 }
270 }
271 }
272
273 const versions = b: {
274 const versions_len = contents[index];
275 index += 1;
276
277 const versions = try arena.alloc(Version, versions_len);
278 var i: u8 = 0;
279 while (i < versions.len) : (i += 1) {
280 versions[i] = .{
281 .major = contents[index + 0],
282 .minor = contents[index + 1],
283 .patch = contents[index + 2],
284 };
285 index += 3;
286 }
287 break :b versions;
288 };
289
290 const targets = b: {
291 const targets_len = contents[index];
292 index += 1;
293
294 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
295 var i: u8 = 0;
296 while (i < targets.len) : (i += 1) {
297 const target_name = mem.sliceTo(contents[index..], 0);
298 index += target_name.len + 1;
299
300 var component_it = mem.tokenizeScalar(u8, target_name, '-');
301 const arch_name = component_it.next() orelse {
302 log.err("abilists: expected arch name", .{});
303 return error.ZigInstallationCorrupt;
304 };
305 const os_name = component_it.next() orelse {
306 log.err("abilists: expected OS name", .{});
307 return error.ZigInstallationCorrupt;
308 };
309 const abi_name = component_it.next() orelse {
310 log.err("abilists: expected ABI name", .{});
311 return error.ZigInstallationCorrupt;
312 };
313 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
314 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
315 return error.ZigInstallationCorrupt;
316 };
317 if (!mem.eql(u8, os_name, "netbsd")) {
318 log.err("abilists: expected OS 'netbsd', found '{s}'", .{os_name});
319 return error.ZigInstallationCorrupt;
320 }
321 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
322 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
323 return error.ZigInstallationCorrupt;
324 };
325
326 targets[i] = .{
327 .arch = arch_tag,
328 .os = .netbsd,
329 .abi = abi_tag,
330 };
331 }
332 break :b targets;
333 };
334
335 const abi = try arena.create(ABI);
336 abi.* = .{
337 .all_versions = versions,
338 .all_targets = targets,
339 .inclusions = contents[index..],
340 .arena_state = arena_allocator.state,
341 };
342 return abi;
343}
344
345pub const BuiltSharedObjects = struct {
346 lock: Cache.Lock,
347 dir_path: Path,
348
349 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
350 self.lock.release();
351 gpa.free(self.dir_path.sub_path);
352 self.* = undefined;
353 }
354};
355
356fn wordDirective(target: std.Target) []const u8 {
357 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
358 // according to the target word size. But no; that would just make too much sense.
359 return if (target.ptrBitWidth() == 64) ".quad" else ".long";
360}
361
362/// TODO replace anyerror with explicit error set, recording user-friendly errors with
363/// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
364pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
365 // See also glibc.zig which this code is based on.
366
367 const tracy = trace(@src());
368 defer tracy.end();
369
370 if (!build_options.have_llvm) {
371 return error.ZigCompilerNotBuiltWithLLVMExtensions;
372 }
373
374 const gpa = comp.gpa;
375
376 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
377 defer arena_allocator.deinit();
378 const arena = arena_allocator.allocator();
379
380 const target = comp.getTarget();
381 const target_version = target.os.version_range.semver.min;
382
383 // Use the global cache directory.
384 var cache: Cache = .{
385 .gpa = gpa,
386 .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
387 };
388 cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
389 cache.addPrefix(comp.zig_lib_directory);
390 cache.addPrefix(comp.global_cache_directory);
391 defer cache.manifest_dir.close();
392
393 var man = cache.obtain();
394 defer man.deinit();
395 man.hash.addBytes(build_options.version);
396 man.hash.add(target.cpu.arch);
397 man.hash.add(target.abi);
398 man.hash.add(target_version);
399
400 const full_abilists_path = try comp.zig_lib_directory.join(arena, &.{abilists_path});
401 const abilists_index = try man.addFile(full_abilists_path, abilists_max_size);
402
403 if (try man.hit()) {
404 const digest = man.final();
405
406 return queueSharedObjects(comp, .{
407 .lock = man.toOwnedLock(),
408 .dir_path = .{
409 .root_dir = comp.global_cache_directory,
410 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
411 },
412 });
413 }
414
415 const digest = man.final();
416 const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
417
418 var o_directory: Compilation.Directory = .{
419 .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
420 .path = try comp.global_cache_directory.join(arena, &.{o_sub_path}),
421 };
422 defer o_directory.handle.close();
423
424 const abilists_contents = man.files.keys()[abilists_index].contents.?;
425 const metadata = try loadMetaData(gpa, abilists_contents);
426 defer metadata.destroy(gpa);
427
428 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
429 if (targ.arch == target.cpu.arch and
430 targ.os == target.os.tag and
431 targ.abi == target.abi)
432 {
433 break i;
434 }
435 } else {
436 unreachable; // std.zig.target.available_libcs prevents us from getting here
437 };
438
439 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
440 switch (ver.order(target_version)) {
441 .eq => break i,
442 .lt => continue,
443 .gt => {
444 // TODO Expose via compile error mechanism instead of log.
445 log.warn("invalid target NetBSD libc version: {}", .{target_version});
446 return error.InvalidTargetLibCVersion;
447 },
448 }
449 } else blk: {
450 const latest_index = metadata.all_versions.len - 1;
451 log.warn("zig cannot build new NetBSD libc version {}; providing instead {}", .{
452 target_version, metadata.all_versions[latest_index],
453 });
454 break :blk latest_index;
455 };
456
457 var stubs_asm = std.ArrayList(u8).init(gpa);
458 defer stubs_asm.deinit();
459
460 for (libs, 0..) |lib, lib_i| {
461 stubs_asm.shrinkRetainingCapacity(0);
462
463 const stubs_writer = stubs_asm.writer();
464
465 try stubs_writer.writeAll(".text\n");
466
467 var sym_i: usize = 0;
468 var sym_name_buf = std.ArrayList(u8).init(arena);
469 var opt_symbol_name: ?[]const u8 = null;
470
471 var inc_fbs = std.io.fixedBufferStream(metadata.inclusions);
472 var inc_reader = inc_fbs.reader();
473
474 const fn_inclusions_len = try inc_reader.readInt(u16, .little);
475
476 var chosen_ver_index: usize = 255;
477 var chosen_is_weak: bool = undefined;
478
479 while (sym_i < fn_inclusions_len) : (sym_i += 1) {
480 const sym_name = opt_symbol_name orelse n: {
481 sym_name_buf.clearRetainingCapacity();
482 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null);
483
484 opt_symbol_name = sym_name_buf.items;
485 chosen_ver_index = 255;
486
487 break :n sym_name_buf.items;
488 };
489
490 {
491 const targets = try std.leb.readUleb128(u64, inc_reader);
492 var lib_index = try inc_reader.readByte();
493
494 const is_weak = (lib_index & (1 << 6)) != 0;
495 const is_terminal = (lib_index & (1 << 7)) != 0;
496
497 lib_index = @as(u5, @truncate(lib_index));
498
499 // Test whether the inclusion applies to our current library and target.
500 const ok_lib_and_target =
501 (lib_index == lib_i) and
502 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
503
504 while (true) {
505 const byte = try inc_reader.readByte();
506 const last = (byte & 0b1000_0000) != 0;
507 const ver_i = @as(u7, @truncate(byte));
508 if (ok_lib_and_target and ver_i <= target_ver_index and
509 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
510 {
511 chosen_ver_index = ver_i;
512 chosen_is_weak = is_weak;
513 }
514 if (last) break;
515 }
516
517 if (is_terminal) {
518 opt_symbol_name = null;
519 } else continue;
520 }
521
522 if (chosen_ver_index != 255) {
523 // Example:
524 // .balign 4
525 // .globl _Exit
526 // .type _Exit, %function
527 // _Exit: .long 0
528 try stubs_writer.print(
529 \\.balign {d}
530 \\.{s} {s}
531 \\.type {s}, %function
532 \\{s}: {s} 0
533 \\
534 , .{
535 target.ptrBitWidth() / 8,
536 if (chosen_is_weak) "weak" else "globl",
537 sym_name,
538 sym_name,
539 sym_name,
540 wordDirective(target),
541 });
542 }
543 }
544
545 try stubs_writer.writeAll(".data\n");
546
547 const obj_inclusions_len = try inc_reader.readInt(u16, .little);
548
549 sym_i = 0;
550 opt_symbol_name = null;
551
552 var chosen_size: u16 = undefined;
553
554 while (sym_i < obj_inclusions_len) : (sym_i += 1) {
555 const sym_name = opt_symbol_name orelse n: {
556 sym_name_buf.clearRetainingCapacity();
557 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null);
558
559 opt_symbol_name = sym_name_buf.items;
560 chosen_ver_index = 255;
561
562 break :n sym_name_buf.items;
563 };
564
565 {
566 const targets = try std.leb.readUleb128(u64, inc_reader);
567 const size = try std.leb.readUleb128(u16, inc_reader);
568 var lib_index = try inc_reader.readByte();
569
570 const is_weak = (lib_index & (1 << 6)) != 0;
571 const is_terminal = (lib_index & (1 << 7)) != 0;
572
573 lib_index = @as(u5, @truncate(lib_index));
574
575 // Test whether the inclusion applies to our current library and target.
576 const ok_lib_and_target =
577 (lib_index == lib_i) and
578 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
579
580 while (true) {
581 const byte = try inc_reader.readByte();
582 const last = (byte & 0b1000_0000) != 0;
583 const ver_i = @as(u7, @truncate(byte));
584 if (ok_lib_and_target and ver_i <= target_ver_index and
585 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
586 {
587 chosen_ver_index = ver_i;
588 chosen_size = size;
589 chosen_is_weak = is_weak;
590 }
591 if (last) break;
592 }
593
594 if (is_terminal) {
595 opt_symbol_name = null;
596 } else continue;
597 }
598
599 if (chosen_ver_index != 255) {
600 // Example:
601 // .balign 4
602 // .globl malloc_conf
603 // .type malloc_conf, %object
604 // .size malloc_conf, 4
605 // malloc_conf: .fill 4, 1, 0
606 try stubs_writer.print(
607 \\.balign {d}
608 \\.{s} {s}
609 \\.type {s}, %object
610 \\.size {s}, {d}
611 \\{s}: {s} 0
612 \\
613 , .{
614 target.ptrBitWidth() / 8,
615 if (chosen_is_weak) "weak" else "globl",
616 sym_name,
617 sym_name,
618 sym_name,
619 chosen_size,
620 sym_name,
621 wordDirective(target),
622 });
623 }
624 }
625
626 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
627 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
628 try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
629 try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node);
630 }
631
632 man.writeManifest() catch |err| {
633 log.warn("failed to write cache manifest for NetBSD libc stubs: {s}", .{@errorName(err)});
634 };
635
636 return queueSharedObjects(comp, .{
637 .lock = man.toOwnedLock(),
638 .dir_path = .{
639 .root_dir = comp.global_cache_directory,
640 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
641 },
642 });
643}
644
645pub fn sharedObjectsCount() u8 {
646 return libs.len;
647}
648
649fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
650 assert(comp.netbsd_so_files == null);
651 comp.netbsd_so_files = so_files;
652
653 var task_buffer: [libs.len]link.Task = undefined;
654 var task_buffer_i: usize = 0;
655
656 {
657 comp.mutex.lock(); // protect comp.arena
658 defer comp.mutex.unlock();
659
660 for (libs) |lib| {
661 const so_path: Path = .{
662 .root_dir = so_files.dir_path.root_dir,
663 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
664 so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover,
665 }) catch return comp.setAllocFailure(),
666 };
667 task_buffer[task_buffer_i] = .{ .load_dso = so_path };
668 task_buffer_i += 1;
669 }
670 }
671
672 comp.queueLinkTasks(task_buffer[0..task_buffer_i]);
673}
674
675fn buildSharedLib(
676 comp: *Compilation,
677 arena: Allocator,
678 zig_cache_directory: Compilation.Directory,
679 bin_directory: Compilation.Directory,
680 asm_file_basename: []const u8,
681 lib: Lib,
682 prog_node: std.Progress.Node,
683) !void {
684 const tracy = trace(@src());
685 defer tracy.end();
686
687 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
688 const emit_bin = Compilation.EmitLoc{
689 .directory = bin_directory,
690 .basename = basename,
691 };
692 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
693 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
694 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
695
696 const optimize_mode = comp.compilerRtOptMode();
697 const strip = comp.compilerRtStrip();
698 const config = try Compilation.Config.resolve(.{
699 .output_mode = .Lib,
700 .link_mode = .dynamic,
701 .resolved_target = comp.root_mod.resolved_target,
702 .is_test = false,
703 .have_zcu = false,
704 .emit_bin = true,
705 .root_optimize_mode = optimize_mode,
706 .root_strip = strip,
707 .link_libc = false,
708 });
709
710 const root_mod = try Module.create(arena, .{
711 .global_cache_directory = comp.global_cache_directory,
712 .paths = .{
713 .root = .{ .root_dir = comp.zig_lib_directory },
714 .root_src_path = "",
715 },
716 .fully_qualified_name = "root",
717 .inherited = .{
718 .resolved_target = comp.root_mod.resolved_target,
719 .strip = strip,
720 .stack_check = false,
721 .stack_protector = 0,
722 .sanitize_c = .off,
723 .sanitize_thread = false,
724 .red_zone = comp.root_mod.red_zone,
725 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
726 .valgrind = false,
727 .optimize_mode = optimize_mode,
728 .structured_cfg = comp.root_mod.structured_cfg,
729 },
730 .global = config,
731 .cc_argv = &.{},
732 .parent = null,
733 .builtin_mod = null,
734 .builtin_modules = null, // there is only one module in this compilation
735 });
736
737 const c_source_files = [1]Compilation.CSourceFile{
738 .{
739 .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
740 .owner = root_mod,
741 },
742 };
743
744 const sub_compilation = try Compilation.create(comp.gpa, arena, .{
745 .local_cache_directory = zig_cache_directory,
746 .global_cache_directory = comp.global_cache_directory,
747 .zig_lib_directory = comp.zig_lib_directory,
748 .thread_pool = comp.thread_pool,
749 .self_exe_path = comp.self_exe_path,
750 .cache_mode = .incremental,
751 .config = config,
752 .root_mod = root_mod,
753 .root_name = lib.name,
754 .libc_installation = comp.libc_installation,
755 .emit_bin = emit_bin,
756 .emit_h = null,
757 .verbose_cc = comp.verbose_cc,
758 .verbose_link = comp.verbose_link,
759 .verbose_air = comp.verbose_air,
760 .verbose_llvm_ir = comp.verbose_llvm_ir,
761 .verbose_llvm_bc = comp.verbose_llvm_bc,
762 .verbose_cimport = comp.verbose_cimport,
763 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
764 .clang_passthrough_mode = comp.clang_passthrough_mode,
765 .version = version,
766 .soname = soname,
767 .c_source_files = &c_source_files,
768 .skip_linker_dependencies = true,
769 });
770 defer sub_compilation.destroy();
771
772 try comp.updateSubCompilation(sub_compilation, .@"netbsd libc shared object", prog_node);
773}
src/link/Elf.zig+8
......@@ -2070,6 +2070,13 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
20702070 });
20712071 try argv.append(lib_path);
20722072 }
2073 } else if (target.isNetBSDLibC()) {
2074 for (netbsd.libs) |lib| {
2075 const lib_path = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{
2076 comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
2077 });
2078 try argv.append(lib_path);
2079 }
20732080 } else {
20742081 diags.flags.missing_libc = true;
20752082 }
......@@ -5289,6 +5296,7 @@ const gc = @import("Elf/gc.zig");
52895296const glibc = @import("../libs/glibc.zig");
52905297const musl = @import("../libs/musl.zig");
52915298const freebsd = @import("../libs/freebsd.zig");
5299const netbsd = @import("../libs/netbsd.zig");
52925300const link = @import("../link.zig");
52935301const relocatable = @import("Elf/relocatable.zig");
52945302const relocation = @import("Elf/relocation.zig");