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 Lib = struct {
19 name: []const u8,
20 sover: u8,
21 removed_in: ?Version = null,
22};
23
24pub const ABI = struct {
25 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
26 all_targets: []const std.zig.target.ArchOsAbi,
27 /// The bytes from the file verbatim, starting from the u16 number
28 /// of function inclusions.
29 inclusions: []const u8,
30 arena_state: std.heap.ArenaAllocator.State,
31
32 pub fn destroy(abi: *ABI, gpa: Allocator) void {
33 abi.arena_state.promote(gpa).deinit();
34 }
35};
36
37// The order of the elements in this array defines the linking order.
38pub const libs = [_]Lib{
39 .{ .name = "m", .sover = 6 },
40 .{ .name = "c", .sover = 6 },
41 .{ .name = "ld", .sover = 2 },
42 .{ .name = "resolv", .sover = 2 },
43 .{ .name = "pthread", .sover = 0, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
44 .{ .name = "dl", .sover = 2, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
45 .{ .name = "rt", .sover = 1, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
46 .{ .name = "util", .sover = 1, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
47};
48
49pub const LoadMetaDataError = error{
50 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
51 ZigInstallationCorrupt,
52 OutOfMemory,
53};
54
55pub const abilists_path = "libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++ "abilists";
56pub const abilists_max_size = 800 * 1024; // Bigger than this and something is definitely borked.
57
58/// This function will emit a log error when there is a problem with the zig
59/// installation and then return `error.ZigInstallationCorrupt`.
60pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
61 const tracy = trace(@src());
62 defer tracy.end();
63
64 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
65 errdefer arena_allocator.deinit();
66 const arena = arena_allocator.allocator();
67
68 var index: usize = 0;
69
70 {
71 const libs_len = contents[index];
72 index += 1;
73
74 var i: u8 = 0;
75 while (i < libs_len) : (i += 1) {
76 const lib_name = mem.sliceTo(contents[index..], 0);
77 index += lib_name.len + 1;
78
79 if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
80 log.err("libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++
81 "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
82 return error.ZigInstallationCorrupt;
83 }
84 }
85 }
86
87 const versions = b: {
88 const versions_len = contents[index];
89 index += 1;
90
91 const versions = try arena.alloc(Version, versions_len);
92 var i: u8 = 0;
93 while (i < versions.len) : (i += 1) {
94 versions[i] = .{
95 .major = contents[index + 0],
96 .minor = contents[index + 1],
97 .patch = contents[index + 2],
98 };
99 index += 3;
100 }
101 break :b versions;
102 };
103
104 const targets = b: {
105 const targets_len = contents[index];
106 index += 1;
107
108 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
109 var i: u8 = 0;
110 while (i < targets.len) : (i += 1) {
111 const target_name = mem.sliceTo(contents[index..], 0);
112 index += target_name.len + 1;
113
114 var component_it = mem.tokenizeScalar(u8, target_name, '-');
115 const arch_name = component_it.next() orelse {
116 log.err("abilists: expected arch name", .{});
117 return error.ZigInstallationCorrupt;
118 };
119 const os_name = component_it.next() orelse {
120 log.err("abilists: expected OS name", .{});
121 return error.ZigInstallationCorrupt;
122 };
123 const abi_name = component_it.next() orelse {
124 log.err("abilists: expected ABI name", .{});
125 return error.ZigInstallationCorrupt;
126 };
127 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
128 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
129 return error.ZigInstallationCorrupt;
130 };
131 if (!mem.eql(u8, os_name, "linux")) {
132 log.err("abilists: expected OS 'linux', found '{s}'", .{os_name});
133 return error.ZigInstallationCorrupt;
134 }
135 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
136 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
137 return error.ZigInstallationCorrupt;
138 };
139
140 targets[i] = .{
141 .arch = arch_tag,
142 .os = .linux,
143 .abi = abi_tag,
144 };
145 }
146 break :b targets;
147 };
148
149 const abi = try arena.create(ABI);
150 abi.* = .{
151 .all_versions = versions,
152 .all_targets = targets,
153 .inclusions = contents[index..],
154 .arena_state = arena_allocator.state,
155 };
156 return abi;
157}
158
159pub const CrtFile = enum {
160 scrt1_o,
161 libc_nonshared_a,
162};
163
164/// TODO replace anyerror with explicit error set, recording user-friendly errors with
165/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
166pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
167 if (!build_options.have_llvm) {
168 return error.ZigCompilerNotBuiltWithLLVMExtensions;
169 }
170 const gpa = comp.gpa;
171 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
172 defer arena_allocator.deinit();
173 const arena = arena_allocator.allocator();
174
175 const target = &comp.root_mod.resolved_target.result;
176 const target_ver = target.os.versionRange().gnuLibCVersion().?;
177 const nonshared_stat = target_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) != .gt;
178 const start_old_init_fini = target_ver.order(.{ .major = 2, .minor = 33, .patch = 0 }) != .gt;
179
180 // In all cases in this function, we add the C compiler flags to
181 // cache_exempt_flags rather than extra_flags, because these arguments
182 // depend on only properties that are already covered by the cache
183 // manifest. Including these arguments in the cache could only possibly
184 // waste computation and create false negatives.
185
186 switch (crt_file) {
187 .scrt1_o => {
188 const start_o: Compilation.CSourceFile = blk: {
189 var args = std.array_list.Managed([]const u8).init(arena);
190 try add_include_dirs(comp, arena, &args);
191 try args.appendSlice(&[_][]const u8{
192 "-w", // Disable all warnings.
193 "-D_LIBC_REENTRANT",
194 "-include",
195 try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
196 "-DMODULE_NAME=libc",
197 "-include",
198 try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
199 "-DPIC",
200 "-DSHARED",
201 "-DTOP_NAMESPACE=glibc",
202 "-DASSEMBLER",
203 "-Wa,--noexecstack",
204 });
205 const src_path = if (start_old_init_fini) "start-2.33.S" else "start.S";
206 break :blk .{
207 .src_path = try start_asm_path(comp, arena, src_path),
208 .cache_exempt_flags = args.items,
209 .owner = undefined,
210 };
211 };
212 const abi_note_o: Compilation.CSourceFile = blk: {
213 var args = std.array_list.Managed([]const u8).init(arena);
214 try args.appendSlice(&[_][]const u8{
215 "-I",
216 try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
217 });
218 try add_include_dirs(comp, arena, &args);
219 try args.appendSlice(&[_][]const u8{
220 "-w", // Disable all warnings.
221 "-D_LIBC_REENTRANT",
222 "-DMODULE_NAME=libc",
223 "-DTOP_NAMESPACE=glibc",
224 "-DASSEMBLER",
225 "-Wa,--noexecstack",
226 });
227 break :blk .{
228 .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
229 .cache_exempt_flags = args.items,
230 .owner = undefined,
231 };
232 };
233 const init_o: Compilation.CSourceFile = blk: {
234 var args = std.array_list.Managed([]const u8).init(arena);
235 try args.appendSlice(&[_][]const u8{
236 "-w", // Disable all warnings.
237 });
238 break :blk .{
239 .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "init.c"),
240 .cache_exempt_flags = args.items,
241 .owner = undefined,
242 };
243 };
244 var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o };
245 const basename = if (comp.config.output_mode == .Exe and !comp.config.pie) "crt1" else "Scrt1";
246 return comp.build_crt_file(basename, .Obj, .@"glibc Scrt1.o", prog_node, &files, .{});
247 },
248 .libc_nonshared_a => {
249 const s = path.sep_str;
250 const Dep = struct {
251 path: []const u8,
252 include: bool = true,
253 };
254 const deps = [_]Dep{
255 .{ .path = lib_libc_glibc ++ "stdlib" ++ s ++ "atexit.c" },
256 .{ .path = lib_libc_glibc ++ "stdlib" ++ s ++ "at_quick_exit.c" },
257 .{ .path = lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread" ++ s ++ "pthread_atfork.c" },
258 .{ .path = lib_libc_glibc ++ "debug" ++ s ++ "stack_chk_fail_local.c" },
259
260 // libc_nonshared.a redirected stat functions to xstat until glibc 2.33,
261 // when they were finally versioned like other symbols.
262 .{
263 .path = lib_libc_glibc ++ "io" ++ s ++ "stat-2.32.c",
264 .include = nonshared_stat,
265 },
266 .{
267 .path = lib_libc_glibc ++ "io" ++ s ++ "fstat-2.32.c",
268 .include = nonshared_stat,
269 },
270 .{
271 .path = lib_libc_glibc ++ "io" ++ s ++ "lstat-2.32.c",
272 .include = nonshared_stat,
273 },
274 .{
275 .path = lib_libc_glibc ++ "io" ++ s ++ "stat64-2.32.c",
276 .include = nonshared_stat,
277 },
278 .{
279 .path = lib_libc_glibc ++ "io" ++ s ++ "fstat64-2.32.c",
280 .include = nonshared_stat,
281 },
282 .{
283 .path = lib_libc_glibc ++ "io" ++ s ++ "lstat64-2.32.c",
284 .include = nonshared_stat,
285 },
286 .{
287 .path = lib_libc_glibc ++ "io" ++ s ++ "fstatat-2.32.c",
288 .include = nonshared_stat,
289 },
290 .{
291 .path = lib_libc_glibc ++ "io" ++ s ++ "fstatat64-2.32.c",
292 .include = nonshared_stat,
293 },
294 .{
295 .path = lib_libc_glibc ++ "io" ++ s ++ "mknodat-2.32.c",
296 .include = nonshared_stat,
297 },
298 .{
299 .path = lib_libc_glibc ++ "io" ++ s ++ "mknod-2.32.c",
300 .include = nonshared_stat,
301 },
302
303 // __libc_start_main used to require statically linked init/fini callbacks
304 // until glibc 2.34 when they were assimilated into the shared library.
305 .{
306 .path = lib_libc_glibc ++ "csu" ++ s ++ "elf-init-2.33.c",
307 .include = start_old_init_fini,
308 },
309 };
310
311 var files_buf: [deps.len]Compilation.CSourceFile = undefined;
312 var files_index: usize = 0;
313
314 for (deps) |dep| {
315 if (!dep.include) continue;
316
317 var args = std.array_list.Managed([]const u8).init(arena);
318 try args.appendSlice(&[_][]const u8{
319 "-w", // Disable all warnings.
320 "-std=gnu11",
321 "-fgnu89-inline",
322 "-fmerge-all-constants",
323 "-frounding-math",
324 "-fno-common",
325 "-fmath-errno",
326 "-ftls-model=initial-exec",
327 "-Qunused-arguments",
328 });
329 try add_include_dirs(comp, arena, &args);
330
331 try args.append("-DNO_INITFINI");
332
333 if (target.cpu.arch == .x86) {
334 // This prevents i386/sysdep.h from trying to do some
335 // silly and unnecessary inline asm hack that uses weird
336 // syntax that clang does not support.
337 try args.append("-DCAN_USE_REGISTER_ASM_EBP");
338 }
339
340 try args.appendSlice(&[_][]const u8{
341 "-D_LIBC_REENTRANT",
342 "-include",
343 try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
344 "-DMODULE_NAME=libc",
345 "-include",
346 try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
347 "-DPIC",
348 "-DLIBC_NONSHARED=1",
349 "-DTOP_NAMESPACE=glibc",
350 });
351 files_buf[files_index] = .{
352 .src_path = try lib_path(comp, arena, dep.path),
353 .cache_exempt_flags = args.items,
354 .owner = undefined,
355 };
356 files_index += 1;
357 }
358 const files = files_buf[0..files_index];
359 return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files, .{});
360 },
361 }
362}
363
364fn start_asm_path(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
365 const arch = comp.getTarget().cpu.arch;
366 const is_ppc = arch.isPowerPC();
367 const is_aarch64 = arch.isAARCH64();
368 const is_sparc = arch.isSPARC();
369 const is_64 = comp.getTarget().ptrBitWidth() == 64;
370
371 const s = path.sep_str;
372
373 var result = std.array_list.Managed(u8).init(arena);
374 try result.appendSlice(comp.dirs.zig_lib.path orelse ".");
375 try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
376 if (is_sparc) {
377 if (is_64) {
378 try result.appendSlice("sparc" ++ s ++ "sparc64");
379 } else {
380 try result.appendSlice("sparc" ++ s ++ "sparc32");
381 }
382 } else if (arch.isArm()) {
383 try result.appendSlice("arm");
384 } else if (arch.isMIPS()) {
385 try result.appendSlice("mips");
386 } else if (arch == .x86_64) {
387 try result.appendSlice("x86_64");
388 } else if (arch == .x86) {
389 try result.appendSlice("i386");
390 } else if (is_aarch64) {
391 try result.appendSlice("aarch64");
392 } else if (arch.isRISCV()) {
393 try result.appendSlice("riscv");
394 } else if (is_ppc) {
395 if (is_64) {
396 try result.appendSlice("powerpc" ++ s ++ "powerpc64");
397 } else {
398 try result.appendSlice("powerpc" ++ s ++ "powerpc32");
399 }
400 } else if (arch == .s390x) {
401 try result.appendSlice("s390");
402 } else if (arch.isLoongArch()) {
403 try result.appendSlice("loongarch");
404 } else if (arch == .m68k) {
405 try result.appendSlice("m68k");
406 } else if (arch == .arc) {
407 try result.appendSlice("arc");
408 } else if (arch == .csky) {
409 try result.appendSlice("csky" ++ s ++ "abiv2");
410 }
411
412 try result.appendSlice(s);
413 try result.appendSlice(basename);
414 return result.items;
415}
416
417fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.array_list.Managed([]const u8)) error{OutOfMemory}!void {
418 const target = comp.getTarget();
419 const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
420
421 const s = path.sep_str;
422
423 try args.append("-I");
424 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
425
426 if (target.os.tag == .linux) {
427 try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
428 }
429
430 if (opt_nptl) |nptl| {
431 try add_include_dirs_arch(arena, args, target, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
432 }
433
434 if (target.os.tag == .linux) {
435 try args.append("-I");
436 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
437 "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
438
439 try args.append("-I");
440 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
441 "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
442 try args.append("-I");
443 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
444 "unix" ++ s ++ "sysv" ++ s ++ "linux"));
445 }
446 if (opt_nptl) |nptl| {
447 try args.append("-I");
448 try args.append(try path.join(arena, &.{ comp.dirs.zig_lib.path orelse ".", lib_libc_glibc ++ "sysdeps", nptl }));
449 }
450
451 try args.append("-I");
452 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
453
454 try args.append("-I");
455 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
456
457 try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
458
459 try args.append("-I");
460 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
461
462 try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
463
464 try args.append("-I");
465 try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
466
467 try args.append("-I");
468 try args.append(try path.join(arena, &[_][]const u8{ comp.dirs.zig_lib.path orelse ".", lib_libc ++ "glibc" }));
469
470 try args.append("-I");
471 try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{
472 comp.dirs.zig_lib.path orelse ".",
473 std.zig.target.glibcArchNameHeaders(target.cpu.arch),
474 @tagName(target.os.tag),
475 std.zig.target.glibcAbiNameHeaders(target.abi),
476 }));
477
478 try args.append("-I");
479 try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
480
481 const arch_name = std.zig.target.osArchName(target);
482 try args.append("-I");
483 try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{
484 comp.dirs.zig_lib.path orelse ".", arch_name,
485 }));
486
487 try args.append("-I");
488 try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
489}
490
491fn add_include_dirs_arch(
492 arena: Allocator,
493 args: *std.array_list.Managed([]const u8),
494 target: *const std.Target,
495 opt_nptl: ?[]const u8,
496 dir: []const u8,
497) error{OutOfMemory}!void {
498 const arch = target.cpu.arch;
499 const is_x86 = arch.isX86();
500 const is_aarch64 = arch.isAARCH64();
501 const is_ppc = arch.isPowerPC();
502 const is_sparc = arch.isSPARC();
503 const is_64 = target.ptrBitWidth() == 64;
504
505 const s = path.sep_str;
506
507 if (is_x86) {
508 if (arch == .x86_64) {
509 if (opt_nptl) |nptl| {
510 try args.append("-I");
511 try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
512 } else {
513 if (target.abi == .gnux32) {
514 try args.append("-I");
515 try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", "x32" }));
516 }
517 try args.append("-I");
518 try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
519 }
520 } else if (arch == .x86) {
521 if (opt_nptl) |nptl| {
522 try args.append("-I");
523 try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
524 } else {
525 try args.append("-I");
526 try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
527 }
528 }
529 if (opt_nptl) |nptl| {
530 try args.append("-I");
531 try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
532 } else {
533 try args.append("-I");
534 try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
535 }
536 } else if (arch.isArm()) {
537 if (opt_nptl) |nptl| {
538 try args.append("-I");
539 try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
540 } else {
541 try args.append("-I");
542 try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
543 }
544 } else if (arch.isMIPS()) {
545 if (opt_nptl) |nptl| {
546 try args.append("-I");
547 try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
548 } else {
549 if (is_64) {
550 try args.append("-I");
551 try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
552 } else {
553 try args.append("-I");
554 try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
555 }
556 try args.append("-I");
557 try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
558 }
559 } else if (is_sparc) {
560 if (opt_nptl) |nptl| {
561 try args.append("-I");
562 try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
563 } else {
564 if (is_64) {
565 try args.append("-I");
566 try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
567 } else {
568 try args.append("-I");
569 try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
570 }
571 try args.append("-I");
572 try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
573 }
574 } else if (is_aarch64) {
575 if (opt_nptl) |nptl| {
576 try args.append("-I");
577 try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
578 } else {
579 try args.append("-I");
580 try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
581 }
582 } else if (is_ppc) {
583 if (opt_nptl) |nptl| {
584 try args.append("-I");
585 try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
586 } else {
587 if (is_64) {
588 try args.append("-I");
589 try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
590 } else {
591 try args.append("-I");
592 try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
593 }
594 try args.append("-I");
595 try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
596 }
597 } else if (arch.isRISCV()) {
598 if (opt_nptl) |nptl| {
599 try args.append("-I");
600 try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
601 } else {
602 try args.append("-I");
603 try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
604 }
605 } else if (arch == .s390x) {
606 if (opt_nptl) |nptl| {
607 try args.append("-I");
608 try args.append(try path.join(arena, &[_][]const u8{ dir, "s390", nptl }));
609 } else {
610 try args.append("-I");
611 try args.append(try path.join(arena, &[_][]const u8{ dir, "s390" }));
612 }
613 } else if (arch.isLoongArch()) {
614 try args.append("-I");
615 try args.append(try path.join(arena, &[_][]const u8{ dir, "loongarch" }));
616 } else if (arch == .m68k) {
617 if (opt_nptl) |nptl| {
618 try args.append("-I");
619 try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k", nptl }));
620 } else {
621 // coldfire ABI support requires: https://github.com/ziglang/zig/issues/20690
622 try args.append("-I");
623 try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k" ++ s ++ "m680x0" }));
624 try args.append("-I");
625 try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k" }));
626 }
627 } else if (arch == .arc) {
628 try args.append("-I");
629 try args.append(try path.join(arena, &[_][]const u8{ dir, "arc" }));
630 } else if (arch == .csky) {
631 try args.append("-I");
632 try args.append(try path.join(arena, &[_][]const u8{ dir, "csky" }));
633 }
634}
635
636const lib_libc = "libc" ++ path.sep_str;
637const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
638
639fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
640 return path.join(arena, &.{ comp.dirs.zig_lib.path orelse ".", sub_path });
641}
642
643pub const BuiltSharedObjects = struct {
644 lock: Cache.Lock,
645 dir_path: Path,
646
647 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator, io: Io) void {
648 self.lock.release(io);
649 gpa.free(self.dir_path.sub_path);
650 self.* = undefined;
651 }
652};
653
654const all_map_basename = "all.map";
655
656fn wordDirective(target: *const std.Target) []const u8 {
657 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
658 // according to the target word size. But no; that would just make too much sense.
659 return if (target.ptrBitWidth() == 64) ".quad" else ".long";
660}
661
662/// TODO replace anyerror with explicit error set, recording user-friendly errors with
663/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
664pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
665 const tracy = trace(@src());
666 defer tracy.end();
667
668 if (!build_options.have_llvm) {
669 return error.ZigCompilerNotBuiltWithLLVMExtensions;
670 }
671
672 const gpa = comp.gpa;
673 const io = comp.io;
674
675 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
676 defer arena_allocator.deinit();
677 const arena = arena_allocator.allocator();
678
679 const target = comp.getTarget();
680 const target_version = target.os.versionRange().gnuLibCVersion().?;
681
682 // Use the global cache directory.
683 var cache: Cache = .{
684 .gpa = gpa,
685 .io = io,
686 .manifest_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, "h", .{}),
687 .cwd = comp.dirs.cwd,
688 };
689 cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
690 cache.addPrefix(comp.dirs.zig_lib);
691 cache.addPrefix(comp.dirs.global_cache);
692 defer cache.manifest_dir.close(io);
693
694 var man = cache.obtain();
695 defer man.deinit();
696 man.hash.addBytes(build_options.version);
697 man.hash.add(target.cpu.arch);
698 man.hash.add(target.abi);
699 man.hash.add(target_version);
700
701 const abilists_index = try man.addFilePath(.{
702 .root_dir = comp.dirs.zig_lib,
703 .sub_path = abilists_path,
704 }, abilists_max_size);
705
706 if (try man.hit(prog_node)) {
707 const digest = man.final();
708
709 return queueSharedObjects(comp, .{
710 .lock = man.toOwnedLock(),
711 .dir_path = .{
712 .root_dir = comp.dirs.global_cache,
713 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
714 },
715 });
716 }
717
718 const digest = man.final();
719 const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
720
721 var o_directory: Cache.Directory = .{
722 .handle = try comp.dirs.global_cache.handle.createDirPathOpen(io, o_sub_path, .{}),
723 .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}),
724 };
725 defer o_directory.handle.close(io);
726
727 const abilists_contents = man.files.keys()[abilists_index].contents.?;
728 const metadata = try loadMetaData(gpa, abilists_contents);
729 defer metadata.destroy(gpa);
730
731 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
732 if (targ.arch == target.cpu.arch and
733 targ.os == target.os.tag and
734 targ.abi == target.abi)
735 {
736 break i;
737 }
738 } else {
739 unreachable; // std.zig.target.available_libcs prevents us from getting here
740 };
741
742 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
743 switch (ver.order(target_version)) {
744 .eq => break i,
745 .lt => continue,
746 .gt => {
747 // TODO Expose via compile error mechanism instead of log.
748 log.warn("invalid target glibc version: {f}", .{target_version});
749 return error.InvalidTargetGLibCVersion;
750 },
751 }
752 } else blk: {
753 const latest_index = metadata.all_versions.len - 1;
754 log.warn("zig cannot build new glibc version {f}; providing instead {f}", .{
755 target_version, metadata.all_versions[latest_index],
756 });
757 break :blk latest_index;
758 };
759
760 {
761 var map_contents = std.array_list.Managed(u8).init(arena);
762 for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| {
763 if (ver.patch == 0) {
764 try map_contents.print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
765 } else {
766 try map_contents.print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
767 }
768 }
769 try o_directory.handle.writeFile(io, .{ .sub_path = all_map_basename, .data = map_contents.items });
770 map_contents.deinit(); // The most recent allocation of an arena can be freed :)
771 }
772
773 var stubs_asm = std.array_list.Managed(u8).init(gpa);
774 defer stubs_asm.deinit();
775
776 for (libs, 0..) |lib, lib_i| {
777 if (lib.removed_in) |rem_in| {
778 if (target_version.order(rem_in) != .lt) continue;
779 }
780
781 stubs_asm.shrinkRetainingCapacity(0);
782 try stubs_asm.appendSlice(".text\n");
783
784 var sym_i: usize = 0;
785 var sym_name_buf: Io.Writer.Allocating = .init(arena);
786 var opt_symbol_name: ?[]const u8 = null;
787 var versions_buffer: [32]u8 = undefined;
788 var versions_len: usize = undefined;
789
790 // There can be situations where there are multiple inclusions for the same symbol with
791 // partially overlapping versions, due to different target lists. For example:
792 //
793 // lgammal:
794 // library: libm.so
795 // versions: 2.4 2.23
796 // targets: ... powerpc64-linux-gnu s390x-linux-gnu
797 // lgammal:
798 // library: libm.so
799 // versions: 2.2 2.23
800 // targets: sparc64-linux-gnu s390x-linux-gnu
801 //
802 // If we don't handle this, we end up writing the default `lgammal` symbol for version 2.33
803 // twice, which causes a "duplicate symbol" assembler error.
804 var versions_written: std.array_hash_map.Auto(Version, void) = .empty;
805
806 var inc_reader: Io.Reader = .fixed(metadata.inclusions);
807
808 const fn_inclusions_len = try inc_reader.takeInt(u16, .little);
809
810 while (sym_i < fn_inclusions_len) : (sym_i += 1) {
811 const sym_name = opt_symbol_name orelse n: {
812 sym_name_buf.clearRetainingCapacity();
813 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
814 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
815 inc_reader.toss(1);
816
817 opt_symbol_name = sym_name_buf.written();
818 versions_buffer = undefined;
819 versions_len = 0;
820
821 break :n sym_name_buf.written();
822 };
823 const targets = try inc_reader.takeLeb128(u64);
824 var lib_index = try inc_reader.takeByte();
825
826 const is_terminal = (lib_index & (1 << 7)) != 0;
827 if (is_terminal) {
828 lib_index &= ~@as(u8, 1 << 7);
829 opt_symbol_name = null;
830 }
831
832 // Test whether the inclusion applies to our current library and target.
833 const ok_lib_and_target =
834 (lib_index == lib_i) and
835 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
836
837 while (true) {
838 const byte = try inc_reader.takeByte();
839 const last = (byte & 0b1000_0000) != 0;
840 const ver_i = @as(u7, @truncate(byte));
841 if (ok_lib_and_target and ver_i <= target_ver_index) {
842 versions_buffer[versions_len] = ver_i;
843 versions_len += 1;
844 }
845 if (last) break;
846 }
847
848 if (!is_terminal) continue;
849
850 // Pick the default symbol version:
851 // - If there are no versions, don't emit it
852 // - Take the greatest one <= than the target one
853 // - If none of them is <= than the
854 // specified one don't pick any default version
855 if (versions_len == 0) continue;
856 var chosen_def_ver_index: u8 = 255;
857 {
858 var ver_buf_i: u8 = 0;
859 while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
860 const ver_index = versions_buffer[ver_buf_i];
861 if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
862 chosen_def_ver_index = ver_index;
863 }
864 }
865 }
866
867 versions_written.clearRetainingCapacity();
868 try versions_written.ensureTotalCapacity(arena, versions_len);
869
870 {
871 var ver_buf_i: u8 = 0;
872 while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
873 // Example:
874 // .balign 4
875 // .globl _Exit_2_2_5
876 // .type _Exit_2_2_5, %function
877 // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5, remove
878 // _Exit_2_2_5: .long 0
879 const ver_index = versions_buffer[ver_buf_i];
880 const ver = metadata.all_versions[ver_index];
881
882 if (versions_written.getOrPutAssumeCapacity(ver).found_existing) continue;
883
884 // Default symbol version definition vs normal symbol version definition
885 const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
886 const at_sign_str: []const u8 = if (want_default) "@@" else "@";
887 if (ver.patch == 0) {
888 const sym_plus_ver = try std.fmt.allocPrint(
889 arena,
890 "{s}_{d}_{d}",
891 .{ sym_name, ver.major, ver.minor },
892 );
893 try stubs_asm.print(
894 \\.balign {d}
895 \\.globl {s}
896 \\.type {s}, %function
897 \\.symver {s}, {s}{s}GLIBC_{d}.{d}, remove
898 \\{s}: {s} 0
899 \\
900 , .{
901 target.ptrBitWidth() / 8,
902 sym_plus_ver,
903 sym_plus_ver,
904 sym_plus_ver,
905 sym_name,
906 at_sign_str,
907 ver.major,
908 ver.minor,
909 sym_plus_ver,
910 wordDirective(target),
911 });
912 } else {
913 const sym_plus_ver = try std.fmt.allocPrint(
914 arena,
915 "{s}_{d}_{d}_{d}",
916 .{ sym_name, ver.major, ver.minor, ver.patch },
917 );
918 try stubs_asm.print(
919 \\.balign {d}
920 \\.globl {s}
921 \\.type {s}, %function
922 \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}, remove
923 \\{s}: {s} 0
924 \\
925 , .{
926 target.ptrBitWidth() / 8,
927 sym_plus_ver,
928 sym_plus_ver,
929 sym_plus_ver,
930 sym_name,
931 at_sign_str,
932 ver.major,
933 ver.minor,
934 ver.patch,
935 sym_plus_ver,
936 wordDirective(target),
937 });
938 }
939 }
940 }
941 }
942
943 try stubs_asm.appendSlice(".rodata\n");
944
945 // For some targets, the real `libc.so.6` will contain a weak reference to `_IO_stdin_used`,
946 // making the linker put the symbol in the dynamic symbol table. We likewise need to emit a
947 // reference to it here for that effect, or it will not show up, which in turn will cause
948 // the real glibc to think that the program was built against an ancient `FILE` structure
949 // (pre-glibc 2.1).
950 //
951 // Note that glibc only compiles in the legacy compatibility code for some targets; it
952 // depends on what is defined in the `shlib-versions` file for the particular architecture
953 // and ABI. Those files are preprocessed by 2 separate tools during the glibc build to get
954 // the final `abi-versions.h`, so it would be quite brittle to try to condition our emission
955 // of the `_IO_stdin_used` reference in the exact same way. The only downside of emitting
956 // the reference unconditionally is that it ends up being unused for newer targets; it
957 // otherwise has no negative effect.
958 //
959 // glibc uses a weak reference because it has to work with programs compiled against pre-2.1
960 // versions where the symbol didn't exist. We only care about modern glibc versions, so use
961 // a strong reference.
962 if (std.mem.eql(u8, lib.name, "c")) {
963 try stubs_asm.print(
964 \\.balign {d}
965 \\.globl _IO_stdin_used
966 \\{s} _IO_stdin_used
967 \\
968 , .{
969 target.ptrBitWidth() / 8,
970 wordDirective(target),
971 });
972 }
973
974 try stubs_asm.appendSlice(".data\n");
975
976 const obj_inclusions_len = try inc_reader.takeInt(u16, .little);
977
978 var sizes = try arena.alloc(u16, metadata.all_versions.len);
979
980 sym_i = 0;
981 opt_symbol_name = null;
982 versions_buffer = undefined;
983 versions_len = undefined;
984 while (sym_i < obj_inclusions_len) : (sym_i += 1) {
985 const sym_name = opt_symbol_name orelse n: {
986 sym_name_buf.clearRetainingCapacity();
987 _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
988 assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
989 inc_reader.toss(1);
990
991 opt_symbol_name = sym_name_buf.written();
992 versions_buffer = undefined;
993 versions_len = 0;
994
995 break :n sym_name_buf.written();
996 };
997 const targets = try inc_reader.takeLeb128(u64);
998 const size = try inc_reader.takeLeb128(u16);
999 var lib_index = try inc_reader.takeByte();
1000
1001 const is_terminal = (lib_index & (1 << 7)) != 0;
1002 if (is_terminal) {
1003 lib_index &= ~@as(u8, 1 << 7);
1004 opt_symbol_name = null;
1005 }
1006
1007 // Test whether the inclusion applies to our current library and target.
1008 const ok_lib_and_target =
1009 (lib_index == lib_i) and
1010 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
1011
1012 while (true) {
1013 const byte = try inc_reader.takeByte();
1014 const last = (byte & 0b1000_0000) != 0;
1015 const ver_i = @as(u7, @truncate(byte));
1016 if (ok_lib_and_target and ver_i <= target_ver_index) {
1017 versions_buffer[versions_len] = ver_i;
1018 versions_len += 1;
1019 sizes[ver_i] = size;
1020 }
1021 if (last) break;
1022 }
1023
1024 if (!is_terminal) continue;
1025
1026 // Pick the default symbol version:
1027 // - If there are no versions, don't emit it
1028 // - Take the greatest one <= than the target one
1029 // - If none of them is <= than the
1030 // specified one don't pick any default version
1031 if (versions_len == 0) continue;
1032 var chosen_def_ver_index: u8 = 255;
1033 {
1034 var ver_buf_i: u8 = 0;
1035 while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
1036 const ver_index = versions_buffer[ver_buf_i];
1037 if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
1038 chosen_def_ver_index = ver_index;
1039 }
1040 }
1041 }
1042
1043 versions_written.clearRetainingCapacity();
1044 try versions_written.ensureTotalCapacity(arena, versions_len);
1045
1046 {
1047 var ver_buf_i: u8 = 0;
1048 while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
1049 // Example:
1050 // .balign 4
1051 // .globl environ_2_2_5
1052 // .type environ_2_2_5, %object
1053 // .size environ_2_2_5, 4
1054 // .symver environ_2_2_5, environ@@GLIBC_2.2.5, remove
1055 // environ_2_2_5: .fill 4, 1, 0
1056 const ver_index = versions_buffer[ver_buf_i];
1057 const ver = metadata.all_versions[ver_index];
1058
1059 if (versions_written.getOrPutAssumeCapacity(ver).found_existing) continue;
1060
1061 // Default symbol version definition vs normal symbol version definition
1062 const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
1063 const at_sign_str: []const u8 = if (want_default) "@@" else "@";
1064 if (ver.patch == 0) {
1065 const sym_plus_ver = try std.fmt.allocPrint(
1066 arena,
1067 "{s}_{d}_{d}",
1068 .{ sym_name, ver.major, ver.minor },
1069 );
1070 try stubs_asm.print(
1071 \\.balign {d}
1072 \\.globl {s}
1073 \\.type {s}, %object
1074 \\.size {s}, {d}
1075 \\.symver {s}, {s}{s}GLIBC_{d}.{d}, remove
1076 \\{s}: .fill {d}, 1, 0
1077 \\
1078 , .{
1079 target.ptrBitWidth() / 8,
1080 sym_plus_ver,
1081 sym_plus_ver,
1082 sym_plus_ver,
1083 sizes[ver_index],
1084 sym_plus_ver,
1085 sym_name,
1086 at_sign_str,
1087 ver.major,
1088 ver.minor,
1089 sym_plus_ver,
1090 sizes[ver_index],
1091 });
1092 } else {
1093 const sym_plus_ver = try std.fmt.allocPrint(
1094 arena,
1095 "{s}_{d}_{d}_{d}",
1096 .{ sym_name, ver.major, ver.minor, ver.patch },
1097 );
1098 try stubs_asm.print(
1099 \\.balign {d}
1100 \\.globl {s}
1101 \\.type {s}, %object
1102 \\.size {s}, {d}
1103 \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}, remove
1104 \\{s}: .fill {d}, 1, 0
1105 \\
1106 , .{
1107 target.ptrBitWidth() / 8,
1108 sym_plus_ver,
1109 sym_plus_ver,
1110 sym_plus_ver,
1111 sizes[ver_index],
1112 sym_plus_ver,
1113 sym_name,
1114 at_sign_str,
1115 ver.major,
1116 ver.minor,
1117 ver.patch,
1118 sym_plus_ver,
1119 sizes[ver_index],
1120 });
1121 }
1122 }
1123 }
1124 }
1125
1126 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
1127 const asm_file_basename = std.mem.print(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
1128 try o_directory.handle.writeFile(io, .{ .sub_path = asm_file_basename, .data = stubs_asm.items });
1129 try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node);
1130 }
1131
1132 man.writeManifest() catch |err| {
1133 log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
1134 };
1135
1136 return queueSharedObjects(comp, .{
1137 .lock = man.toOwnedLock(),
1138 .dir_path = .{
1139 .root_dir = comp.dirs.global_cache,
1140 .sub_path = try gpa.dupe(u8, "o" ++ path.sep_str ++ digest),
1141 },
1142 });
1143}
1144
1145fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) std.Io.Cancelable!void {
1146 const io = comp.io;
1147 const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
1148
1149 assert(comp.glibc_so_files == null);
1150 comp.glibc_so_files = so_files;
1151
1152 var task_buffer: [libs.len]link.PrelinkTask = undefined;
1153 var task_buffer_i: usize = 0;
1154
1155 {
1156 comp.mutex.lockUncancelable(io); // protect comp.arena
1157 defer comp.mutex.unlock(io);
1158
1159 for (libs) |lib| {
1160 if (lib.removed_in) |rem_in| {
1161 if (target_version.order(rem_in) != .lt) continue;
1162 }
1163 const so_path: Path = .{
1164 .root_dir = so_files.dir_path.root_dir,
1165 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
1166 so_files.dir_path.sub_path, path.sep, lib.name, lib.sover,
1167 }) catch return comp.setAllocFailure(),
1168 };
1169 task_buffer[task_buffer_i] = .{ .load_dso = so_path };
1170 task_buffer_i += 1;
1171 }
1172 }
1173
1174 try comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
1175}
1176
1177fn buildSharedLib(
1178 comp: *Compilation,
1179 arena: Allocator,
1180 bin_directory: Cache.Directory,
1181 asm_file_basename: []const u8,
1182 lib: Lib,
1183 prog_node: std.Progress.Node,
1184) !void {
1185 const tracy = trace(@src());
1186 defer tracy.end();
1187
1188 const io = comp.io;
1189 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
1190 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
1191 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
1192 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
1193
1194 const optimize_mode = comp.compilerRtOptMode();
1195 const strip = comp.compilerRtStrip();
1196 const config = try Compilation.Config.resolve(.{
1197 .output_mode = .Lib,
1198 .link_mode = .dynamic,
1199 .resolved_target = comp.root_mod.resolved_target,
1200 .is_test = false,
1201 .have_zcu = false,
1202 .emit_bin = true,
1203 .root_optimize_mode = optimize_mode,
1204 .root_strip = strip,
1205 .link_libc = false,
1206 });
1207
1208 const root_mod = try Module.create(arena, .{
1209 .paths = .{
1210 .root = .zig_lib_root,
1211 .root_src_path = "",
1212 },
1213 .fully_qualified_name = "root",
1214 .inherited = .{
1215 .resolved_target = comp.root_mod.resolved_target,
1216 .strip = strip,
1217 .stack_check = false,
1218 .stack_protector = 0,
1219 .sanitize_c = .off,
1220 .sanitize_thread = false,
1221 .red_zone = comp.root_mod.red_zone,
1222 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
1223 .valgrind = false,
1224 .optimize_mode = optimize_mode,
1225 },
1226 .global = config,
1227 .cc_argv = &.{},
1228 .parent = null,
1229 });
1230
1231 const c_source_files = [1]Compilation.CSourceFile{
1232 .{
1233 .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
1234 .owner = root_mod,
1235 },
1236 };
1237
1238 const misc_task: Compilation.MiscTask = .@"glibc shared object";
1239
1240 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
1241 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
1242 .thread_limit = comp.thread_limit,
1243 .dirs = comp.dirs.withoutLocalCache(),
1244 .self_exe_path = comp.self_exe_path,
1245 // Because we manually cache the whole set of objects, we don't cache the individual objects
1246 // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path.
1247 .cache_mode = .none,
1248 .config = config,
1249 .root_mod = root_mod,
1250 .root_name = lib.name,
1251 .libc_installation = comp.libc_installation,
1252 .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) },
1253 .verbose_cc = comp.verbose_cc,
1254 .verbose_link = comp.verbose_link,
1255 .verbose_air = comp.verbose_air,
1256 .verbose_llvm_ir = comp.verbose_llvm_ir,
1257 .verbose_llvm_bc = comp.verbose_llvm_bc,
1258 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
1259 .clang_passthrough_mode = comp.clang_passthrough_mode,
1260 .version = version,
1261 .version_script = .{
1262 .root_dir = bin_directory,
1263 .sub_path = all_map_basename,
1264 },
1265 .soname = soname,
1266 .c_source_files = &c_source_files,
1267 .skip_linker_dependencies = true,
1268 .environ_map = comp.environ_map,
1269 }) catch |err| switch (err) {
1270 error.CreateFail => {
1271 comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
1272 return error.AlreadyReported;
1273 },
1274 else => |e| return e,
1275 };
1276 defer sub_compilation.destroy();
1277
1278 try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
1279}
1280
1281pub fn needsCrt0(output_mode: std.lang.OutputMode) ?CrtFile {
1282 return switch (output_mode) {
1283 .Obj, .Lib => null,
1284 .Exe => .scrt1_o,
1285 };
1286}