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