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