1const std = @import("std");
2const Io = std.Io;
3const Allocator = std.mem.Allocator;
4const mem = std.mem;
5const log = std.log;
6const path = std.Io.Dir.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("../Module.zig");
16const link = @import("../link.zig");
17
18pub const CrtFile = enum {
19 scrt0_o,
20};
21
22pub fn needsCrt0(output_mode: std.lang.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.dirs.zig_lib.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.dirs.zig_lib.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/// lockAndSetMiscFailure and returning error.AlreadyReported. 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.array_list.Managed([]const u8).init(arena);
73 try cflags.appendSlice(&.{
74 "-DHAVE_INITFINI_ARRAY",
75 "-w", // Disable all warnings.
76 });
77
78 var acflags = std.array_list.Managed([]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 std.zig.target.netbsdAbiNameHeaders(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 ++ "riscv" ++ path.sep_str ++ "crt0.S",
163 .flags = acflags.items,
164 .condition = target.cpu.arch.isRISCV(),
165 },
166
167 .{
168 .path = "arch" ++ path.sep_str ++ "sparc" ++ path.sep_str ++ "crt0.S",
169 .flags = acflags.items,
170 .condition = target.cpu.arch == .sparc,
171 },
172
173 .{
174 .path = "arch" ++ path.sep_str ++ "sparc64" ++ path.sep_str ++ "crt0.S",
175 .flags = acflags.items,
176 .condition = target.cpu.arch == .sparc64,
177 },
178
179 .{
180 .path = "arch" ++ path.sep_str ++ "i386" ++ path.sep_str ++ "crt0.S",
181 .flags = acflags.items,
182 .condition = target.cpu.arch == .x86,
183 },
184
185 .{
186 .path = "arch" ++ path.sep_str ++ "x86_64" ++ path.sep_str ++ "crt0.S",
187 .flags = acflags.items,
188 .condition = target.cpu.arch == .x86_64,
189 },
190 };
191
192 var files_buf: [sources.len]Compilation.CSourceFile = undefined;
193 var files_index: usize = 0;
194 for (sources) |file| {
195 if (!file.condition) continue;
196
197 files_buf[files_index] = .{
198 .src_path = try csuPath(comp, arena, file.path),
199 .cache_exempt_flags = file.flags,
200 .owner = undefined,
201 };
202 files_index += 1;
203 }
204 const files = files_buf[0..files_index];
205
206 return comp.build_crt_file("crt0", .Obj, .@"netbsd libc Scrt0.o", prog_node, files, .{
207 .pic = true,
208 });
209 },
210 }
211}
212
213pub const Lib = struct {
214 name: []const u8,
215 sover: u8,
216};
217
218pub const libs = [_]Lib{
219 .{ .name = "m", .sover = 0 },
220 .{ .name = "pthread", .sover = 1 },
221 .{ .name = "c", .sover = 12 },
222 .{ .name = "rt", .sover = 1 },
223 .{ .name = "ld", .sover = 0 },
224 .{ .name = "util", .sover = 7 },
225 .{ .name = "execinfo", .sover = 0 },
226};
227
228pub const ABI = struct {
229 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
230 all_targets: []const std.zig.target.ArchOsAbi,
231 /// The bytes from the file verbatim, starting from the u16 number
232 /// of function inclusions.
233 inclusions: []const u8,
234 arena_state: std.heap.ArenaAllocator.State,
235
236 pub fn destroy(abi: *ABI, gpa: Allocator) void {
237 abi.arena_state.promote(gpa).deinit();
238 }
239};
240
241pub const LoadMetaDataError = error{
242 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
243 ZigInstallationCorrupt,
244 OutOfMemory,
245};
246
247pub const abilists_path = "libc" ++ path.sep_str ++ "netbsd" ++ path.sep_str ++ "abilists";
248pub const abilists_max_size = 300 * 1024; // Bigger than this and something is definitely borked.
249
250/// This function will emit a log error when there is a problem with the zig
251/// installation and then return `error.ZigInstallationCorrupt`.
252pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
253 const tracy = trace(@src());
254 defer tracy.end();
255
256 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
257 errdefer arena_allocator.deinit();
258 const arena = arena_allocator.allocator();
259
260 var index: usize = 0;
261
262 {
263 const libs_len = contents[index];
264 index += 1;
265
266 var i: u8 = 0;
267 while (i < libs_len) : (i += 1) {
268 const lib_name = mem.sliceTo(contents[index..], 0);
269 index += lib_name.len + 1;
270
271 if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
272 log.err("libc" ++ path.sep_str ++ "netbsd" ++ path.sep_str ++
273 "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
274 return error.ZigInstallationCorrupt;
275 }
276 }
277 }
278
279 const versions = b: {
280 const versions_len = contents[index];
281 index += 1;
282
283 const versions = try arena.alloc(Version, versions_len);
284 var i: u8 = 0;
285 while (i < versions.len) : (i += 1) {
286 versions[i] = .{
287 .major = contents[index + 0],
288 .minor = contents[index + 1],
289 .patch = contents[index + 2],
290 };
291 index += 3;
292 }
293 break :b versions;
294 };
295
296 const targets = b: {
297 const targets_len = contents[index];
298 index += 1;
299
300 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
301 var i: u8 = 0;
302 while (i < targets.len) : (i += 1) {
303 const target_name = mem.sliceTo(contents[index..], 0);
304 index += target_name.len + 1;
305
306 var component_it = mem.tokenizeScalar(u8, target_name, '-');
307 const arch_name = component_it.next() orelse {
308 log.err("abilists: expected arch name", .{});
309 return error.ZigInstallationCorrupt;
310 };
311 const os_name = component_it.next() orelse {
312 log.err("abilists: expected OS name", .{});
313 return error.ZigInstallationCorrupt;
314 };
315 const abi_name = component_it.next() orelse {
316 log.err("abilists: expected ABI name", .{});
317 return error.ZigInstallationCorrupt;
318 };
319 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
320 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
321 return error.ZigInstallationCorrupt;
322 };
323 if (!mem.eql(u8, os_name, "netbsd")) {
324 log.err("abilists: expected OS 'netbsd', found '{s}'", .{os_name});
325 return error.ZigInstallationCorrupt;
326 }
327 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
328 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
329 return error.ZigInstallationCorrupt;
330 };
331
332 targets[i] = .{
333 .arch = arch_tag,
334 .os = .netbsd,
335 .abi = abi_tag,
336 };
337 }
338 break :b targets;
339 };
340
341 const abi = try arena.create(ABI);
342 abi.* = .{
343 .all_versions = versions,
344 .all_targets = targets,
345 .inclusions = contents[index..],
346 .arena_state = arena_allocator.state,
347 };
348 return abi;
349}
350
351pub const BuiltSharedObjects = struct {
352 lock: Cache.Lock,
353 dir_path: Path,
354
355 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator, io: Io) void {
356 self.lock.release(io);
357 gpa.free(self.dir_path.sub_path);
358 self.* = undefined;
359 }
360};
361
362fn wordDirective(target: *const std.Target) []const u8 {
363 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
364 // according to the target word size. But no; that would just make too much sense.
365 return if (target.ptrBitWidth() == 64) ".quad" else ".long";
366}
367
368/// TODO replace anyerror with explicit error set, recording user-friendly errors with
369/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
370pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
371 // See also glibc.zig which this code is based on.
372
373 const tracy = trace(@src());
374 defer tracy.end();
375
376 if (!build_options.have_llvm) {
377 return error.ZigCompilerNotBuiltWithLLVMExtensions;
378 }
379
380 const gpa = comp.gpa;
381 const io = comp.io;
382
383 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
384 defer arena_allocator.deinit();
385 const arena = arena_allocator.allocator();
386
387 const target = comp.getTarget();
388 const target_version = target.os.version_range.semver.min;
389
390 // Use the global cache directory.
391 var cache: Cache = .{
392 .gpa = gpa,
393 .io = io,
394 .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
395 .cwd = comp.dirs.cwd,
396 };
397 cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
398 cache.addPrefix(comp.dirs.zig_lib);
399 cache.addPrefix(comp.dirs.global_cache);
400 defer cache.manifest_dir.close(io);
401
402 var man = cache.obtain();
403 defer man.deinit();
404 man.hash.addBytes(build_options.version);
405 man.hash.add(target.cpu.arch);
406 man.hash.add(target.abi);
407 man.hash.add(target_version);
408
409 const abilists_index = try man.addFilePath(.{
410 .root_dir = comp.dirs.zig_lib,
411 .sub_path = abilists_path,
412 }, abilists_max_size);
413
414 if (try man.hit(prog_node)) {
415 const digest = man.final();
416
417 return queueSharedObjects(comp, .{
418 .lock = man.toOwnedLock(),
419 .dir_path = .{
420 .root_dir = comp.dirs.global_cache,
421 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
422 },
423 });
424 }
425
426 const digest = man.final();
427 const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
428
429 var o_directory: Cache.Directory = .{
430 .handle = try comp.dirs.global_cache.handle.createDirPathOpen(io, o_sub_path, .{}),
431 .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}),
432 };
433 defer o_directory.handle.close(io);
434
435 const abilists_contents = man.files.keys()[abilists_index].contents.?;
436 const metadata = try loadMetaData(gpa, abilists_contents);
437 defer metadata.destroy(gpa);
438
439 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
440 if (targ.arch == target.cpu.arch and
441 targ.os == target.os.tag and
442 targ.abi == target.abi)
443 {
444 break i;
445 }
446 } else {
447 unreachable; // std.zig.target.available_libcs prevents us from getting here
448 };
449
450 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
451 switch (ver.order(target_version)) {
452 .eq => break i,
453 .lt => continue,
454 .gt => {
455 // TODO Expose via compile error mechanism instead of log.
456 log.warn("invalid target NetBSD libc version: {f}", .{target_version});
457 return error.InvalidTargetLibCVersion;
458 },
459 }
460 } else blk: {
461 const latest_index = metadata.all_versions.len - 1;
462 log.warn("zig cannot build new NetBSD libc version {f}; providing instead {f}", .{
463 target_version, metadata.all_versions[latest_index],
464 });
465 break :blk latest_index;
466 };
467
468 var stubs_asm = std.array_list.Managed(u8).init(gpa);
469 defer stubs_asm.deinit();
470
471 for (libs, 0..) |lib, lib_i| {
472 stubs_asm.shrinkRetainingCapacity(0);
473
474 try stubs_asm.appendSlice(".text\n");
475
476 var sym_i: usize = 0;
477 var sym_name_buf: std.Io.Writer.Allocating = .init(arena);
478 var opt_symbol_name: ?[]const u8 = null;
479
480 var inc_reader: std.Io.Reader = .fixed(metadata.inclusions);
481
482 const fn_inclusions_len = try inc_reader.takeInt(u16, .little);
483
484 var chosen_ver_index: usize = 255;
485 var chosen_is_weak: bool = undefined;
486
487 while (sym_i < fn_inclusions_len) : (sym_i += 1) {
488 const sym_name = opt_symbol_name orelse n: {
489 sym_name_buf.clearRetainingCapacity();
490 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
491 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
492 inc_reader.toss(1);
493
494 opt_symbol_name = sym_name_buf.written();
495 chosen_ver_index = 255;
496
497 break :n sym_name_buf.written();
498 };
499
500 {
501 const targets = try inc_reader.takeLeb128(u64);
502 var lib_index = try inc_reader.takeByte();
503
504 const is_weak = (lib_index & (1 << 6)) != 0;
505 const is_terminal = (lib_index & (1 << 7)) != 0;
506
507 lib_index = @as(u5, @truncate(lib_index));
508
509 // Test whether the inclusion applies to our current library and target.
510 const ok_lib_and_target =
511 (lib_index == lib_i) and
512 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
513
514 while (true) {
515 const byte = try inc_reader.takeByte();
516 const last = (byte & 0b1000_0000) != 0;
517 const ver_i = @as(u7, @truncate(byte));
518 if (ok_lib_and_target and ver_i <= target_ver_index and
519 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
520 {
521 chosen_ver_index = ver_i;
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 _Exit
536 // .type _Exit, %function
537 // _Exit: .long 0
538 try stubs_asm.print(
539 \\.balign {d}
540 \\.{s} {s}
541 \\.type {s}, %function
542 \\{s}: {s} 0
543 \\
544 , .{
545 target.ptrBitWidth() / 8,
546 if (chosen_is_weak) "weak" else "globl",
547 sym_name,
548 sym_name,
549 sym_name,
550 wordDirective(target),
551 });
552 }
553 }
554
555 try stubs_asm.appendSlice(".data\n");
556
557 const obj_inclusions_len = try inc_reader.takeInt(u16, .little);
558
559 sym_i = 0;
560 opt_symbol_name = null;
561
562 var chosen_size: u16 = undefined;
563
564 while (sym_i < obj_inclusions_len) : (sym_i += 1) {
565 const sym_name = opt_symbol_name orelse n: {
566 sym_name_buf.clearRetainingCapacity();
567 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
568 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
569 inc_reader.toss(1);
570
571 opt_symbol_name = sym_name_buf.written();
572 chosen_ver_index = 255;
573
574 break :n sym_name_buf.written();
575 };
576
577 {
578 const targets = try inc_reader.takeLeb128(u64);
579 const size = try inc_reader.takeLeb128(u16);
580 var lib_index = try inc_reader.takeByte();
581
582 const is_weak = (lib_index & (1 << 6)) != 0;
583 const is_terminal = (lib_index & (1 << 7)) != 0;
584
585 lib_index = @as(u5, @truncate(lib_index));
586
587 // Test whether the inclusion applies to our current library and target.
588 const ok_lib_and_target =
589 (lib_index == lib_i) and
590 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
591
592 while (true) {
593 const byte = try inc_reader.takeByte();
594 const last = (byte & 0b1000_0000) != 0;
595 const ver_i = @as(u7, @truncate(byte));
596 if (ok_lib_and_target and ver_i <= target_ver_index and
597 (chosen_ver_index == 255 or ver_i > chosen_ver_index))
598 {
599 chosen_ver_index = ver_i;
600 chosen_size = size;
601 chosen_is_weak = is_weak;
602 }
603 if (last) break;
604 }
605
606 if (is_terminal) {
607 opt_symbol_name = null;
608 } else continue;
609 }
610
611 if (chosen_ver_index != 255) {
612 // Example:
613 // .balign 4
614 // .globl malloc_conf
615 // .type malloc_conf, %object
616 // .size malloc_conf, 4
617 // malloc_conf: .fill 4, 1, 0
618 try stubs_asm.print(
619 \\.balign {d}
620 \\.{s} {s}
621 \\.type {s}, %object
622 \\.size {s}, {d}
623 \\{s}: {s} 0
624 \\
625 , .{
626 target.ptrBitWidth() / 8,
627 if (chosen_is_weak) "weak" else "globl",
628 sym_name,
629 sym_name,
630 sym_name,
631 chosen_size,
632 sym_name,
633 wordDirective(target),
634 });
635 }
636 }
637
638 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
639 const asm_file_basename = std.mem.print(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
640 try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items });
641 try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node);
642 }
643
644 man.writeManifest() catch |err| {
645 log.warn("failed to write cache manifest for NetBSD libc stubs: {s}", .{@errorName(err)});
646 };
647
648 return queueSharedObjects(comp, .{
649 .lock = man.toOwnedLock(),
650 .dir_path = .{
651 .root_dir = comp.dirs.global_cache,
652 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
653 },
654 });
655}
656
657fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
658 const io = comp.io;
659 assert(comp.netbsd_so_files == null);
660 comp.netbsd_so_files = so_files;
661
662 var task_buffer: [libs.len]link.PrelinkTask = undefined;
663 var task_buffer_i: usize = 0;
664
665 {
666 comp.mutex.lockUncancelable(io); // protect comp.arena
667 defer comp.mutex.unlock(io);
668
669 for (libs) |lib| {
670 const so_path: Path = .{
671 .root_dir = so_files.dir_path.root_dir,
672 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
673 so_files.dir_path.sub_path, path.sep, lib.name, lib.sover,
674 }) catch return comp.setAllocFailure(),
675 };
676 task_buffer[task_buffer_i] = .{ .load_dso = so_path };
677 task_buffer_i += 1;
678 }
679 }
680
681 try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
682}
683
684fn buildSharedLib(
685 comp: *Compilation,
686 arena: Allocator,
687 bin_directory: Cache.Directory,
688 asm_file_basename: []const u8,
689 lib: Lib,
690 prog_node: std.Progress.Node,
691) !void {
692 const tracy = trace(@src());
693 defer tracy.end();
694
695 const io = comp.io;
696 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
697 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
698 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
699 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
700
701 const optimize_mode = comp.compilerRtOptMode();
702 const strip = comp.compilerRtStrip();
703 const config = try Compilation.Config.resolve(.{
704 .output_mode = .Lib,
705 .link_mode = .dynamic,
706 .resolved_target = comp.root_mod.resolved_target,
707 .is_test = false,
708 .have_zcu = false,
709 .emit_bin = true,
710 .root_optimize_mode = optimize_mode,
711 .root_strip = strip,
712 .link_libc = false,
713 });
714
715 const root_mod = try Module.create(arena, .{
716 .paths = .{
717 .root = .zig_lib_root,
718 .root_src_path = "",
719 },
720 .fully_qualified_name = "root",
721 .inherited = .{
722 .resolved_target = comp.root_mod.resolved_target,
723 .strip = strip,
724 .stack_check = false,
725 .stack_protector = 0,
726 .sanitize_c = .off,
727 .sanitize_thread = false,
728 .red_zone = comp.root_mod.red_zone,
729 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
730 .valgrind = false,
731 .optimize_mode = optimize_mode,
732 },
733 .global = config,
734 .cc_argv = &.{},
735 .parent = null,
736 });
737
738 const c_source_files = [1]Compilation.CSourceFile{
739 .{
740 .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
741 .owner = root_mod,
742 },
743 };
744
745 const misc_task: Compilation.MiscTask = .@"netbsd libc shared object";
746
747 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
748 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
749 .thread_limit = comp.thread_limit,
750 .dirs = comp.dirs.withoutLocalCache(),
751 .self_exe_path = comp.self_exe_path,
752 // Because we manually cache the whole set of objects, we don't cache the individual objects
753 // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path.
754 .cache_mode = .none,
755 .config = config,
756 .root_mod = root_mod,
757 .root_name = lib.name,
758 .libc_installation = comp.libc_installation,
759 .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) },
760 .verbose_cc = comp.verbose_cc,
761 .verbose_link = comp.verbose_link,
762 .verbose_air = comp.verbose_air,
763 .verbose_llvm_ir = comp.verbose_llvm_ir,
764 .verbose_llvm_bc = comp.verbose_llvm_bc,
765 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
766 .clang_passthrough_mode = comp.clang_passthrough_mode,
767 .version = version,
768 .soname = soname,
769 .c_source_files = &c_source_files,
770 .skip_linker_dependencies = true,
771 .environ_map = comp.environ_map,
772 }) catch |err| switch (err) {
773 error.CreateFail => {
774 comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
775 return error.AlreadyReported;
776 },
777 else => |e| return e,
778 };
779 defer sub_compilation.destroy();
780
781 try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
782}