blob 6baa8990 (41922B) - Raw
1 const std = @import("std"); 2 const Allocator = std.mem.Allocator; 3 const mem = std.mem; 4 const log = std.log; 5 const fs = std.fs; 6 const path = fs.path; 7 const assert = std.debug.assert; 8 const Version = std.SemanticVersion; 9 const Path = std.Build.Cache.Path; 10 11 const Compilation = @import("../Compilation.zig"); 12 const build_options = @import("build_options"); 13 const trace = @import("../tracy.zig").trace; 14 const Cache = std.Build.Cache; 15 const Module = @import("../Package/Module.zig"); 16 const link = @import("../link.zig"); 17 18 pub const CrtFile = enum { 19 scrt1_o, 20 }; 21 22 pub 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 => .scrt1_o, 32 }; 33 } 34 35 fn 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 43 fn 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 ++ "freebsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "csu", 47 sub_path, 48 }); 49 } 50 51 fn libcPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 { 52 return path.join(arena, &.{ 53 comp.dirs.zig_lib.path.?, 54 "libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "libc", 55 sub_path, 56 }); 57 } 58 59 /// TODO replace anyerror with explicit error set, recording user-friendly errors with 60 /// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. 61 pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void { 62 if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; 63 64 const gpa = comp.gpa; 65 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 66 defer arena_allocator.deinit(); 67 const arena = arena_allocator.allocator(); 68 69 const target = &comp.root_mod.resolved_target.result; 70 71 // In all cases in this function, we add the C compiler flags to 72 // cache_exempt_flags rather than extra_flags, because these arguments 73 // depend on only properties that are already covered by the cache 74 // manifest. Including these arguments in the cache could only possibly 75 // waste computation and create false negatives. 76 77 switch (crt_file) { 78 .scrt1_o => { 79 var cflags = std.ArrayList([]const u8).init(arena); 80 try cflags.appendSlice(&.{ 81 "-O2", 82 "-fno-common", 83 "-std=gnu99", 84 "-DPIC", 85 "-w", // Disable all warnings. 86 }); 87 88 if (target.cpu.arch.isPowerPC64()) { 89 try cflags.append("-mlongcall"); 90 } 91 92 var acflags = std.ArrayList([]const u8).init(arena); 93 try acflags.appendSlice(&.{ 94 "-DLOCORE", 95 // See `Compilation.addCCArgs`. 96 try std.fmt.allocPrint(arena, "-D__FreeBSD_version={d}", .{target.os.version_range.semver.min.major * 100_000}), 97 }); 98 99 inline for (.{ &cflags, &acflags }) |flags| { 100 try flags.appendSlice(&.{ 101 "-DSTRIP_FBSDID", 102 "-I", 103 try includePath(comp, arena, try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{ 104 std.zig.target.freebsdArchNameHeaders(target.cpu.arch), 105 @tagName(target.os.tag), 106 @tagName(target.abi), 107 })), 108 "-I", 109 try includePath(comp, arena, "generic-freebsd"), 110 "-I", 111 try csuPath(comp, arena, switch (target.cpu.arch) { 112 .arm => "arm", 113 .aarch64 => "aarch64", 114 .powerpc => "powerpc", 115 .powerpc64, .powerpc64le => "powerpc64", 116 .riscv64 => "riscv", 117 .x86 => "i386", 118 .x86_64 => "amd64", 119 else => unreachable, 120 }), 121 "-I", 122 try csuPath(comp, arena, "common"), 123 "-I", 124 try libcPath(comp, arena, "include"), 125 "-Qunused-arguments", 126 }); 127 } 128 129 const sources = [_]struct { 130 path: []const u8, 131 flags: []const []const u8, 132 condition: bool = true, 133 }{ 134 .{ 135 .path = "common" ++ path.sep_str ++ "crtbegin.c", 136 .flags = cflags.items, 137 }, 138 .{ 139 .path = "common" ++ path.sep_str ++ "crtbrand.S", 140 .flags = acflags.items, 141 }, 142 .{ 143 .path = "common" ++ path.sep_str ++ "crtend.c", 144 .flags = cflags.items, 145 }, 146 .{ 147 .path = "common" ++ path.sep_str ++ "feature_note.S", 148 .flags = acflags.items, 149 }, 150 .{ 151 .path = "common" ++ path.sep_str ++ "ignore_init_note.S", 152 .flags = acflags.items, 153 }, 154 155 .{ 156 .path = "arm" ++ path.sep_str ++ "crt1_c.c", 157 .flags = cflags.items, 158 .condition = target.cpu.arch == .arm, 159 }, 160 .{ 161 .path = "arm" ++ path.sep_str ++ "crt1_s.S", 162 .flags = acflags.items, 163 .condition = target.cpu.arch == .arm, 164 }, 165 166 .{ 167 .path = "aarch64" ++ path.sep_str ++ "crt1_c.c", 168 .flags = cflags.items, 169 .condition = target.cpu.arch == .aarch64, 170 }, 171 .{ 172 .path = "aarch64" ++ path.sep_str ++ "crt1_s.S", 173 .flags = acflags.items, 174 .condition = target.cpu.arch == .aarch64, 175 }, 176 177 .{ 178 .path = "powerpc" ++ path.sep_str ++ "crt1_c.c", 179 .flags = cflags.items, 180 .condition = target.cpu.arch == .powerpc, 181 }, 182 .{ 183 .path = "powerpc" ++ path.sep_str ++ "crtsavres.S", 184 .flags = acflags.items, 185 .condition = target.cpu.arch == .powerpc, 186 }, 187 188 .{ 189 .path = "powerpc64" ++ path.sep_str ++ "crt1_c.c", 190 .flags = cflags.items, 191 .condition = target.cpu.arch.isPowerPC64(), 192 }, 193 194 .{ 195 .path = "riscv" ++ path.sep_str ++ "crt1_c.c", 196 .flags = cflags.items, 197 .condition = target.cpu.arch == .riscv64, 198 }, 199 .{ 200 .path = "riscv" ++ path.sep_str ++ "crt1_s.S", 201 .flags = acflags.items, 202 .condition = target.cpu.arch == .riscv64, 203 }, 204 205 .{ 206 .path = "i386" ++ path.sep_str ++ "crt1_c.c", 207 .flags = cflags.items, 208 .condition = target.cpu.arch == .x86, 209 }, 210 .{ 211 .path = "i386" ++ path.sep_str ++ "crt1_s.S", 212 .flags = acflags.items, 213 .condition = target.cpu.arch == .x86, 214 }, 215 216 .{ 217 .path = "amd64" ++ path.sep_str ++ "crt1_c.c", 218 .flags = cflags.items, 219 .condition = target.cpu.arch == .x86_64, 220 }, 221 .{ 222 .path = "amd64" ++ path.sep_str ++ "crt1_s.S", 223 .flags = acflags.items, 224 .condition = target.cpu.arch == .x86_64, 225 }, 226 }; 227 228 var files_buf: [sources.len]Compilation.CSourceFile = undefined; 229 var files_index: usize = 0; 230 for (sources) |file| { 231 if (!file.condition) continue; 232 233 files_buf[files_index] = .{ 234 .src_path = try csuPath(comp, arena, file.path), 235 .cache_exempt_flags = file.flags, 236 .owner = undefined, 237 }; 238 files_index += 1; 239 } 240 const files = files_buf[0..files_index]; 241 242 return comp.build_crt_file( 243 if (comp.config.pie) "Scrt1" else "crt1", 244 .Obj, 245 .@"freebsd libc Scrt1.o", 246 prog_node, 247 files, 248 .{ 249 .omit_frame_pointer = false, 250 .pic = true, 251 }, 252 ); 253 }, 254 } 255 } 256 257 pub const Lib = struct { 258 name: []const u8, 259 sover: u8, 260 }; 261 262 pub const libs = [_]Lib{ 263 .{ .name = "m", .sover = 5 }, 264 .{ .name = "stdthreads", .sover = 0 }, 265 .{ .name = "thr", .sover = 3 }, 266 .{ .name = "c", .sover = 7 }, 267 .{ .name = "dl", .sover = 1 }, 268 .{ .name = "rt", .sover = 1 }, 269 .{ .name = "ld", .sover = 1 }, 270 .{ .name = "util", .sover = 9 }, 271 .{ .name = "execinfo", .sover = 1 }, 272 }; 273 274 pub const ABI = struct { 275 all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current) 276 all_targets: []const std.zig.target.ArchOsAbi, 277 /// The bytes from the file verbatim, starting from the u16 number 278 /// of function inclusions. 279 inclusions: []const u8, 280 arena_state: std.heap.ArenaAllocator.State, 281 282 pub fn destroy(abi: *ABI, gpa: Allocator) void { 283 abi.arena_state.promote(gpa).deinit(); 284 } 285 }; 286 287 pub const LoadMetaDataError = error{ 288 /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data. 289 ZigInstallationCorrupt, 290 OutOfMemory, 291 }; 292 293 pub const abilists_path = "libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ "abilists"; 294 pub const abilists_max_size = 150 * 1024; // Bigger than this and something is definitely borked. 295 296 /// This function will emit a log error when there is a problem with the zig 297 /// installation and then return `error.ZigInstallationCorrupt`. 298 pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI { 299 const tracy = trace(@src()); 300 defer tracy.end(); 301 302 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 303 errdefer arena_allocator.deinit(); 304 const arena = arena_allocator.allocator(); 305 306 var index: usize = 0; 307 308 { 309 const libs_len = contents[index]; 310 index += 1; 311 312 var i: u8 = 0; 313 while (i < libs_len) : (i += 1) { 314 const lib_name = mem.sliceTo(contents[index..], 0); 315 index += lib_name.len + 1; 316 317 if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) { 318 log.err("libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ 319 "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name }); 320 return error.ZigInstallationCorrupt; 321 } 322 } 323 } 324 325 const versions = b: { 326 const versions_len = contents[index]; 327 index += 1; 328 329 const versions = try arena.alloc(Version, versions_len); 330 var i: u8 = 0; 331 while (i < versions.len) : (i += 1) { 332 versions[i] = .{ 333 .major = contents[index + 0], 334 .minor = contents[index + 1], 335 .patch = contents[index + 2], 336 }; 337 index += 3; 338 } 339 break :b versions; 340 }; 341 342 const targets = b: { 343 const targets_len = contents[index]; 344 index += 1; 345 346 const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len); 347 var i: u8 = 0; 348 while (i < targets.len) : (i += 1) { 349 const target_name = mem.sliceTo(contents[index..], 0); 350 index += target_name.len + 1; 351 352 var component_it = mem.tokenizeScalar(u8, target_name, '-'); 353 const arch_name = component_it.next() orelse { 354 log.err("abilists: expected arch name", .{}); 355 return error.ZigInstallationCorrupt; 356 }; 357 const os_name = component_it.next() orelse { 358 log.err("abilists: expected OS name", .{}); 359 return error.ZigInstallationCorrupt; 360 }; 361 const abi_name = component_it.next() orelse { 362 log.err("abilists: expected ABI name", .{}); 363 return error.ZigInstallationCorrupt; 364 }; 365 const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { 366 log.err("abilists: unrecognized arch: '{s}'", .{arch_name}); 367 return error.ZigInstallationCorrupt; 368 }; 369 if (!mem.eql(u8, os_name, "freebsd")) { 370 log.err("abilists: expected OS 'freebsd', found '{s}'", .{os_name}); 371 return error.ZigInstallationCorrupt; 372 } 373 const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse { 374 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name}); 375 return error.ZigInstallationCorrupt; 376 }; 377 378 targets[i] = .{ 379 .arch = arch_tag, 380 .os = .freebsd, 381 .abi = abi_tag, 382 }; 383 } 384 break :b targets; 385 }; 386 387 const abi = try arena.create(ABI); 388 abi.* = .{ 389 .all_versions = versions, 390 .all_targets = targets, 391 .inclusions = contents[index..], 392 .arena_state = arena_allocator.state, 393 }; 394 return abi; 395 } 396 397 pub const BuiltSharedObjects = struct { 398 lock: Cache.Lock, 399 dir_path: Path, 400 401 pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void { 402 self.lock.release(); 403 gpa.free(self.dir_path.sub_path); 404 self.* = undefined; 405 } 406 }; 407 408 const all_map_basename = "all.map"; 409 410 fn wordDirective(target: *const std.Target) []const u8 { 411 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized 412 // according to the target word size. But no; that would just make too much sense. 413 return if (target.ptrBitWidth() == 64) ".quad" else ".long"; 414 } 415 416 /// TODO replace anyerror with explicit error set, recording user-friendly errors with 417 /// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example. 418 pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void { 419 // See also glibc.zig which this code is based on. 420 421 const tracy = trace(@src()); 422 defer tracy.end(); 423 424 if (!build_options.have_llvm) { 425 return error.ZigCompilerNotBuiltWithLLVMExtensions; 426 } 427 428 const gpa = comp.gpa; 429 430 var arena_allocator = std.heap.ArenaAllocator.init(gpa); 431 defer arena_allocator.deinit(); 432 const arena = arena_allocator.allocator(); 433 434 const target = comp.getTarget(); 435 // FreeBSD 7 == FBSD_1.0, ..., FreeBSD 14 == FBSD_1.7 436 const target_version: Version = .{ .major = 1, .minor = target.os.version_range.semver.min.major - 7, .patch = 0 }; 437 438 // Use the global cache directory. 439 var cache: Cache = .{ 440 .gpa = gpa, 441 .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}), 442 }; 443 cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); 444 cache.addPrefix(comp.dirs.zig_lib); 445 cache.addPrefix(comp.dirs.global_cache); 446 defer cache.manifest_dir.close(); 447 448 var man = cache.obtain(); 449 defer man.deinit(); 450 man.hash.addBytes(build_options.version); 451 man.hash.add(target.cpu.arch); 452 man.hash.add(target.abi); 453 man.hash.add(target_version); 454 455 const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path}); 456 const abilists_index = try man.addFile(full_abilists_path, abilists_max_size); 457 458 if (try man.hit()) { 459 const digest = man.final(); 460 461 return queueSharedObjects(comp, .{ 462 .lock = man.toOwnedLock(), 463 .dir_path = .{ 464 .root_dir = comp.dirs.global_cache, 465 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), 466 }, 467 }); 468 } 469 470 const digest = man.final(); 471 const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest }); 472 473 var o_directory: Cache.Directory = .{ 474 .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}), 475 .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}), 476 }; 477 defer o_directory.handle.close(); 478 479 const abilists_contents = man.files.keys()[abilists_index].contents.?; 480 const metadata = try loadMetaData(gpa, abilists_contents); 481 defer metadata.destroy(gpa); 482 483 const target_targ_index = for (metadata.all_targets, 0..) |targ, i| { 484 if (targ.arch == target.cpu.arch and 485 targ.os == target.os.tag and 486 targ.abi == target.abi) 487 { 488 break i; 489 } 490 } else { 491 unreachable; // std.zig.target.available_libcs prevents us from getting here 492 }; 493 494 const target_ver_index = for (metadata.all_versions, 0..) |ver, i| { 495 switch (ver.order(target_version)) { 496 .eq => break i, 497 .lt => continue, 498 .gt => { 499 // TODO Expose via compile error mechanism instead of log. 500 log.warn("invalid target FreeBSD libc version: {f}", .{target_version}); 501 return error.InvalidTargetLibCVersion; 502 }, 503 } 504 } else blk: { 505 const latest_index = metadata.all_versions.len - 1; 506 log.warn("zig cannot build new FreeBSD libc version {f}; providing instead {f}", .{ 507 target_version, metadata.all_versions[latest_index], 508 }); 509 break :blk latest_index; 510 }; 511 512 { 513 var map_contents = std.ArrayList(u8).init(arena); 514 for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| { 515 try map_contents.writer().print("FBSD_{d}.{d} {{ }};\n", .{ ver.major, ver.minor }); 516 } 517 try o_directory.handle.writeFile(.{ .sub_path = all_map_basename, .data = map_contents.items }); 518 map_contents.deinit(); 519 } 520 521 var stubs_asm = std.ArrayList(u8).init(gpa); 522 defer stubs_asm.deinit(); 523 524 for (libs, 0..) |lib, lib_i| { 525 stubs_asm.shrinkRetainingCapacity(0); 526 527 const stubs_writer = stubs_asm.writer(); 528 529 try stubs_writer.writeAll(".text\n"); 530 531 var sym_i: usize = 0; 532 var sym_name_buf = std.ArrayList(u8).init(arena); 533 var opt_symbol_name: ?[]const u8 = null; 534 var versions = try std.DynamicBitSetUnmanaged.initEmpty(arena, metadata.all_versions.len); 535 var weak_linkages = try std.DynamicBitSetUnmanaged.initEmpty(arena, metadata.all_versions.len); 536 537 var inc_fbs = std.io.fixedBufferStream(metadata.inclusions); 538 var inc_reader = inc_fbs.reader(); 539 540 const fn_inclusions_len = try inc_reader.readInt(u16, .little); 541 542 // Pick the default symbol version: 543 // - If there are no versions, don't emit it 544 // - Take the greatest one <= than the target one 545 // - If none of them is <= than the 546 // specified one don't pick any default version 547 var chosen_def_ver_index: usize = 255; 548 var chosen_unversioned_ver_index: usize = 255; 549 550 while (sym_i < fn_inclusions_len) : (sym_i += 1) { 551 const sym_name = opt_symbol_name orelse n: { 552 sym_name_buf.clearRetainingCapacity(); 553 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null); 554 555 opt_symbol_name = sym_name_buf.items; 556 versions.unsetAll(); 557 weak_linkages.unsetAll(); 558 chosen_def_ver_index = 255; 559 chosen_unversioned_ver_index = 255; 560 561 break :n sym_name_buf.items; 562 }; 563 { 564 const targets = try std.leb.readUleb128(u64, inc_reader); 565 var lib_index = try inc_reader.readByte(); 566 567 const is_unversioned = (lib_index & (1 << 5)) != 0; 568 const is_weak = (lib_index & (1 << 6)) != 0; 569 const is_terminal = (lib_index & (1 << 7)) != 0; 570 571 lib_index = @as(u5, @truncate(lib_index)); 572 573 // Test whether the inclusion applies to our current library and target. 574 const ok_lib_and_target = 575 (lib_index == lib_i) and 576 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0); 577 578 while (true) { 579 const byte = try inc_reader.readByte(); 580 const last = (byte & 0b1000_0000) != 0; 581 const ver_i = @as(u7, @truncate(byte)); 582 if (ok_lib_and_target and ver_i <= target_ver_index) { 583 if (is_unversioned) { 584 if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) { 585 chosen_unversioned_ver_index = ver_i; 586 } 587 } else { 588 if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) { 589 chosen_def_ver_index = ver_i; 590 } 591 592 versions.set(ver_i); 593 } 594 595 weak_linkages.setValue(ver_i, is_weak); 596 } 597 if (last) break; 598 } 599 600 if (is_terminal) { 601 opt_symbol_name = null; 602 } else continue; 603 } 604 605 if (chosen_unversioned_ver_index != 255) { 606 // Example: 607 // .balign 4 608 // .globl _Exit 609 // .type _Exit, %function 610 // _Exit: .long 0 611 try stubs_writer.print( 612 \\.balign {d} 613 \\.{s} {s} 614 \\.type {s}, %function 615 \\{s}: {s} 0 616 \\ 617 , .{ 618 target.ptrBitWidth() / 8, 619 if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl", 620 sym_name, 621 sym_name, 622 sym_name, 623 wordDirective(target), 624 }); 625 } 626 627 { 628 var versions_iter = versions.iterator(.{}); 629 while (versions_iter.next()) |ver_index| { 630 // Example: 631 // .balign 4 632 // .globl _Exit_1_0 633 // .type _Exit_1_0, %function 634 // .symver _Exit_1_0, _Exit@@FBSD_1.0, remove 635 // _Exit_1_0: .long 0 636 const ver = metadata.all_versions[ver_index]; 637 const sym_plus_ver = try std.fmt.allocPrint( 638 arena, 639 "{s}_FBSD_{d}_{d}", 640 .{ sym_name, ver.major, ver.minor }, 641 ); 642 643 try stubs_writer.print( 644 \\.balign {d} 645 \\.{s} {s} 646 \\.type {s}, %function 647 \\.symver {s}, {s}{s}FBSD_{d}.{d}, remove 648 \\{s}: {s} 0 649 \\ 650 , .{ 651 target.ptrBitWidth() / 8, 652 if (weak_linkages.isSet(ver_index)) "weak" else "globl", 653 sym_plus_ver, 654 sym_plus_ver, 655 sym_plus_ver, 656 sym_name, 657 // Default symbol version definition vs normal symbol version definition 658 if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@", 659 ver.major, 660 ver.minor, 661 sym_plus_ver, 662 wordDirective(target), 663 }); 664 } 665 } 666 } 667 668 try stubs_writer.writeAll(".data\n"); 669 670 // FreeBSD's `libc.so.7` contains strong references to `__progname` and `environ` which are 671 // defined in the statically-linked startup code. Those references cause the linker to put 672 // the symbols in the dynamic symbol table. We need to create dummy references to them here 673 // to get the same effect. 674 if (std.mem.eql(u8, lib.name, "c")) { 675 try stubs_writer.print( 676 \\.balign {d} 677 \\.globl __progname 678 \\.globl environ 679 \\{s} __progname 680 \\{s} environ 681 \\ 682 , .{ 683 target.ptrBitWidth() / 8, 684 wordDirective(target), 685 wordDirective(target), 686 }); 687 } 688 689 const obj_inclusions_len = try inc_reader.readInt(u16, .little); 690 691 var sizes = try arena.alloc(u16, metadata.all_versions.len); 692 693 sym_i = 0; 694 opt_symbol_name = null; 695 696 while (sym_i < obj_inclusions_len) : (sym_i += 1) { 697 const sym_name = opt_symbol_name orelse n: { 698 sym_name_buf.clearRetainingCapacity(); 699 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null); 700 701 opt_symbol_name = sym_name_buf.items; 702 versions.unsetAll(); 703 weak_linkages.unsetAll(); 704 chosen_def_ver_index = 255; 705 chosen_unversioned_ver_index = 255; 706 707 break :n sym_name_buf.items; 708 }; 709 710 { 711 const targets = try std.leb.readUleb128(u64, inc_reader); 712 const size = try std.leb.readUleb128(u16, inc_reader); 713 var lib_index = try inc_reader.readByte(); 714 715 const is_unversioned = (lib_index & (1 << 5)) != 0; 716 const is_weak = (lib_index & (1 << 6)) != 0; 717 const is_terminal = (lib_index & (1 << 7)) != 0; 718 719 lib_index = @as(u5, @truncate(lib_index)); 720 721 // Test whether the inclusion applies to our current library and target. 722 const ok_lib_and_target = 723 (lib_index == lib_i) and 724 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0); 725 726 while (true) { 727 const byte = try inc_reader.readByte(); 728 const last = (byte & 0b1000_0000) != 0; 729 const ver_i = @as(u7, @truncate(byte)); 730 if (ok_lib_and_target and ver_i <= target_ver_index) { 731 if (is_unversioned) { 732 if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) { 733 chosen_unversioned_ver_index = ver_i; 734 } 735 } else { 736 if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) { 737 chosen_def_ver_index = ver_i; 738 } 739 740 versions.set(ver_i); 741 } 742 743 sizes[ver_i] = size; 744 weak_linkages.setValue(ver_i, is_weak); 745 } 746 if (last) break; 747 } 748 749 if (is_terminal) { 750 opt_symbol_name = null; 751 } else continue; 752 } 753 754 if (chosen_unversioned_ver_index != 255) { 755 // Example: 756 // .balign 4 757 // .globl malloc_conf 758 // .type malloc_conf, %object 759 // .size malloc_conf, 4 760 // malloc_conf: .fill 4, 1, 0 761 try stubs_writer.print( 762 \\.balign {d} 763 \\.{s} {s} 764 \\.type {s}, %object 765 \\.size {s}, {d} 766 \\{s}: {s} 0 767 \\ 768 , .{ 769 target.ptrBitWidth() / 8, 770 if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl", 771 sym_name, 772 sym_name, 773 sym_name, 774 sizes[chosen_unversioned_ver_index], 775 sym_name, 776 wordDirective(target), 777 }); 778 } 779 780 { 781 var versions_iter = versions.iterator(.{}); 782 while (versions_iter.next()) |ver_index| { 783 // Example: 784 // .balign 4 785 // .globl malloc_conf_1_3 786 // .type malloc_conf_1_3, %object 787 // .size malloc_conf_1_3, 4 788 // .symver malloc_conf_1_3, malloc_conf@@FBSD_1.3 789 // malloc_conf_1_3: .fill 4, 1, 0 790 const ver = metadata.all_versions[ver_index]; 791 const sym_plus_ver = try std.fmt.allocPrint( 792 arena, 793 "{s}_FBSD_{d}_{d}", 794 .{ sym_name, ver.major, ver.minor }, 795 ); 796 797 try stubs_asm.writer().print( 798 \\.balign {d} 799 \\.{s} {s} 800 \\.type {s}, %object 801 \\.size {s}, {d} 802 \\.symver {s}, {s}{s}FBSD_{d}.{d} 803 \\{s}: .fill {d}, 1, 0 804 \\ 805 , .{ 806 target.ptrBitWidth() / 8, 807 if (weak_linkages.isSet(ver_index)) "weak" else "globl", 808 sym_plus_ver, 809 sym_plus_ver, 810 sym_plus_ver, 811 sizes[ver_index], 812 sym_plus_ver, 813 sym_name, 814 // Default symbol version definition vs normal symbol version definition 815 if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@", 816 ver.major, 817 ver.minor, 818 sym_plus_ver, 819 sizes[ver_index], 820 }); 821 } 822 } 823 } 824 825 try stubs_writer.writeAll(".tdata\n"); 826 827 const tls_inclusions_len = try inc_reader.readInt(u16, .little); 828 829 sym_i = 0; 830 opt_symbol_name = null; 831 832 while (sym_i < tls_inclusions_len) : (sym_i += 1) { 833 const sym_name = opt_symbol_name orelse n: { 834 sym_name_buf.clearRetainingCapacity(); 835 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null); 836 837 opt_symbol_name = sym_name_buf.items; 838 versions.unsetAll(); 839 weak_linkages.unsetAll(); 840 chosen_def_ver_index = 255; 841 chosen_unversioned_ver_index = 255; 842 843 break :n sym_name_buf.items; 844 }; 845 846 { 847 const targets = try std.leb.readUleb128(u64, inc_reader); 848 const size = try std.leb.readUleb128(u16, inc_reader); 849 var lib_index = try inc_reader.readByte(); 850 851 const is_unversioned = (lib_index & (1 << 5)) != 0; 852 const is_weak = (lib_index & (1 << 6)) != 0; 853 const is_terminal = (lib_index & (1 << 7)) != 0; 854 855 lib_index = @as(u5, @truncate(lib_index)); 856 857 // Test whether the inclusion applies to our current library and target. 858 const ok_lib_and_target = 859 (lib_index == lib_i) and 860 ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0); 861 862 while (true) { 863 const byte = try inc_reader.readByte(); 864 const last = (byte & 0b1000_0000) != 0; 865 const ver_i = @as(u7, @truncate(byte)); 866 if (ok_lib_and_target and ver_i <= target_ver_index) { 867 if (is_unversioned) { 868 if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) { 869 chosen_unversioned_ver_index = ver_i; 870 } 871 } else { 872 if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) { 873 chosen_def_ver_index = ver_i; 874 } 875 876 versions.set(ver_i); 877 } 878 879 sizes[ver_i] = size; 880 weak_linkages.setValue(ver_i, is_weak); 881 } 882 if (last) break; 883 } 884 885 if (is_terminal) { 886 opt_symbol_name = null; 887 } else continue; 888 } 889 890 if (chosen_unversioned_ver_index != 255) { 891 // Example: 892 // .balign 4 893 // .globl _ThreadRuneLocale 894 // .type _ThreadRuneLocale, %object 895 // .size _ThreadRuneLocale, 4 896 // _ThreadRuneLocale: .fill 4, 1, 0 897 try stubs_writer.print( 898 \\.balign {d} 899 \\.{s} {s} 900 \\.type {s}, %tls_object 901 \\.size {s}, {d} 902 \\{s}: {s} 0 903 \\ 904 , .{ 905 target.ptrBitWidth() / 8, 906 if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl", 907 sym_name, 908 sym_name, 909 sym_name, 910 sizes[chosen_unversioned_ver_index], 911 sym_name, 912 wordDirective(target), 913 }); 914 } 915 916 { 917 var versions_iter = versions.iterator(.{}); 918 while (versions_iter.next()) |ver_index| { 919 // Example: 920 // .balign 4 921 // .globl _ThreadRuneLocale_1_3 922 // .type _ThreadRuneLocale_1_3, %tls_object 923 // .size _ThreadRuneLocale_1_3, 4 924 // .symver _ThreadRuneLocale_1_3, _ThreadRuneLocale@@FBSD_1.3 925 // _ThreadRuneLocale_1_3: .fill 4, 1, 0 926 const ver = metadata.all_versions[ver_index]; 927 const sym_plus_ver = try std.fmt.allocPrint( 928 arena, 929 "{s}_FBSD_{d}_{d}", 930 .{ sym_name, ver.major, ver.minor }, 931 ); 932 933 try stubs_writer.print( 934 \\.balign {d} 935 \\.{s} {s} 936 \\.type {s}, %tls_object 937 \\.size {s}, {d} 938 \\.symver {s}, {s}{s}FBSD_{d}.{d} 939 \\{s}: .fill {d}, 1, 0 940 \\ 941 , .{ 942 target.ptrBitWidth() / 8, 943 if (weak_linkages.isSet(ver_index)) "weak" else "globl", 944 sym_plus_ver, 945 sym_plus_ver, 946 sym_plus_ver, 947 sizes[ver_index], 948 sym_plus_ver, 949 sym_name, 950 // Default symbol version definition vs normal symbol version definition 951 if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@", 952 ver.major, 953 ver.minor, 954 sym_plus_ver, 955 sizes[ver_index], 956 }); 957 } 958 } 959 } 960 961 var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "stdthreads", etc. 962 const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; 963 try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); 964 try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node); 965 } 966 967 man.writeManifest() catch |err| { 968 log.warn("failed to write cache manifest for FreeBSD libc stubs: {s}", .{@errorName(err)}); 969 }; 970 971 return queueSharedObjects(comp, .{ 972 .lock = man.toOwnedLock(), 973 .dir_path = .{ 974 .root_dir = comp.dirs.global_cache, 975 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), 976 }, 977 }); 978 } 979 980 pub fn sharedObjectsCount() u8 { 981 return libs.len; 982 } 983 984 fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { 985 assert(comp.freebsd_so_files == null); 986 comp.freebsd_so_files = so_files; 987 988 var task_buffer: [libs.len]link.PrelinkTask = undefined; 989 var task_buffer_i: usize = 0; 990 991 { 992 comp.mutex.lock(); // protect comp.arena 993 defer comp.mutex.unlock(); 994 995 for (libs) |lib| { 996 const so_path: Path = .{ 997 .root_dir = so_files.dir_path.root_dir, 998 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{ 999 so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover, 1000 }) catch return comp.setAllocFailure(), 1001 }; 1002 task_buffer[task_buffer_i] = .{ .load_dso = so_path }; 1003 task_buffer_i += 1; 1004 } 1005 } 1006 1007 comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]); 1008 } 1009 1010 fn buildSharedLib( 1011 comp: *Compilation, 1012 arena: Allocator, 1013 bin_directory: Cache.Directory, 1014 asm_file_basename: []const u8, 1015 lib: Lib, 1016 prog_node: std.Progress.Node, 1017 ) !void { 1018 const tracy = trace(@src()); 1019 defer tracy.end(); 1020 1021 const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }); 1022 const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; 1023 const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); 1024 const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; 1025 const map_file_path = try path.join(arena, &.{ bin_directory.path.?, all_map_basename }); 1026 1027 const optimize_mode = comp.compilerRtOptMode(); 1028 const strip = comp.compilerRtStrip(); 1029 const config = try Compilation.Config.resolve(.{ 1030 .output_mode = .Lib, 1031 .link_mode = .dynamic, 1032 .resolved_target = comp.root_mod.resolved_target, 1033 .is_test = false, 1034 .have_zcu = false, 1035 .emit_bin = true, 1036 .root_optimize_mode = optimize_mode, 1037 .root_strip = strip, 1038 .link_libc = false, 1039 }); 1040 1041 const root_mod = try Module.create(arena, .{ 1042 .paths = .{ 1043 .root = .zig_lib_root, 1044 .root_src_path = "", 1045 }, 1046 .fully_qualified_name = "root", 1047 .inherited = .{ 1048 .resolved_target = comp.root_mod.resolved_target, 1049 .strip = strip, 1050 .stack_check = false, 1051 .stack_protector = 0, 1052 .sanitize_c = .off, 1053 .sanitize_thread = false, 1054 .red_zone = comp.root_mod.red_zone, 1055 .omit_frame_pointer = comp.root_mod.omit_frame_pointer, 1056 .valgrind = false, 1057 .optimize_mode = optimize_mode, 1058 .structured_cfg = comp.root_mod.structured_cfg, 1059 }, 1060 .global = config, 1061 .cc_argv = &.{}, 1062 .parent = null, 1063 }); 1064 1065 const c_source_files = [1]Compilation.CSourceFile{ 1066 .{ 1067 .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }), 1068 .owner = root_mod, 1069 }, 1070 }; 1071 1072 const sub_compilation = try Compilation.create(comp.gpa, arena, .{ 1073 .dirs = comp.dirs.withoutLocalCache(), 1074 .thread_pool = comp.thread_pool, 1075 .self_exe_path = comp.self_exe_path, 1076 // Because we manually cache the whole set of objects, we don't cache the individual objects 1077 // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path. 1078 .cache_mode = .none, 1079 .config = config, 1080 .root_mod = root_mod, 1081 .root_name = lib.name, 1082 .libc_installation = comp.libc_installation, 1083 .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, 1084 .verbose_cc = comp.verbose_cc, 1085 .verbose_link = comp.verbose_link, 1086 .verbose_air = comp.verbose_air, 1087 .verbose_llvm_ir = comp.verbose_llvm_ir, 1088 .verbose_llvm_bc = comp.verbose_llvm_bc, 1089 .verbose_cimport = comp.verbose_cimport, 1090 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, 1091 .clang_passthrough_mode = comp.clang_passthrough_mode, 1092 .version = version, 1093 .version_script = map_file_path, 1094 .soname = soname, 1095 .c_source_files = &c_source_files, 1096 .skip_linker_dependencies = true, 1097 }); 1098 defer sub_compilation.destroy(); 1099 1100 try comp.updateSubCompilation(sub_compilation, .@"freebsd libc shared object", prog_node); 1101 }