authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-28 16:02:18+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-04-29 13:01:33+02:00
log0227253677e6579deb1849554638fd9d8f20d9e3
tree1bd5428a2514140a27bf109d9f4f64b60afee512
parent39064347460d1f8cd72ada1e77618fc2b83f8d0d
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

compiler: link libtsan with -whole-archive

libtsan contains a .preinit_array entry that must run for correctness.

7 files changed, 54 insertions(+), 42 deletions(-)

src/Compilation.zig+6-4
...@@ -7426,7 +7426,7 @@ fn buildOutputFromZig(...@@ -7426,7 +7426,7 @@ fn buildOutputFromZig(
7426 assert(out.* == null);7426 assert(out.* == null);
7427 out.* = crt_file;7427 out.* = crt_file;
74287428
7429 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);7429 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
7430}7430}
74317431
7432pub const CrtFileOptions = struct {7432pub const CrtFileOptions = struct {
...@@ -7560,7 +7560,7 @@ pub fn build_crt_file(...@@ -7560,7 +7560,7 @@ pub fn build_crt_file(
7560 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);7560 try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
75617561
7562 const crt_file = try sub_compilation.toCrtFile();7562 const crt_file = try sub_compilation.toCrtFile();
7563 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);7563 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
75647564
7565 {7565 {
7566 comp.mutex.lockUncancelable(io);7566 comp.mutex.lockUncancelable(io);
...@@ -7570,12 +7570,14 @@ pub fn build_crt_file(...@@ -7570,12 +7570,14 @@ pub fn build_crt_file(
7570 }7570 }
7571}7571}
75727572
7573pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) Io.Cancelable!void {7573/// If `must_link` is set, then static library inputs will have all member objects linked into the
7574/// output, instead of only those required to resolve symbol references.
7575pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, must_link: bool, config: *const Compilation.Config) Io.Cancelable!void {
7574 try comp.queuePrelinkTasks(switch (config.output_mode) {7576 try comp.queuePrelinkTasks(switch (config.output_mode) {
7575 .Exe => unreachable,7577 .Exe => unreachable,
7576 .Obj => &.{.{ .load_object = path }},7578 .Obj => &.{.{ .load_object = path }},
7577 .Lib => &.{switch (config.link_mode) {7579 .Lib => &.{switch (config.link_mode) {
7578 .static => .{ .load_archive = path },7580 .static => .{ .load_archive = .{ .path = path, .must_link = must_link } },
7579 .dynamic => .{ .load_dso = path },7581 .dynamic => .{ .load_dso = path },
7580 }},7582 }},
7581 });7583 });
src/libs/libcxx.zig+2-2
...@@ -295,7 +295,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!...@@ -295,7 +295,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!
295 assert(comp.libcxx_static_lib == null);295 assert(comp.libcxx_static_lib == null);
296 const crt_file = try sub_compilation.toCrtFile();296 const crt_file = try sub_compilation.toCrtFile();
297 comp.libcxx_static_lib = crt_file;297 comp.libcxx_static_lib = crt_file;
298 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);298 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
299}299}
300300
301pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {301pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
...@@ -492,7 +492,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -492,7 +492,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
492 assert(comp.libcxxabi_static_lib == null);492 assert(comp.libcxxabi_static_lib == null);
493 const crt_file = try sub_compilation.toCrtFile();493 const crt_file = try sub_compilation.toCrtFile();
494 comp.libcxxabi_static_lib = crt_file;494 comp.libcxxabi_static_lib = crt_file;
495 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);495 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
496}496}
497497
498pub fn addCxxArgs(498pub fn addCxxArgs(
src/libs/libtsan.zig+2-1
...@@ -320,8 +320,9 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo...@@ -320,8 +320,9 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo
320 },320 },
321 };321 };
322322
323 // libtsan contains `.preinit_array` entries that must run for correctness, hence `must_link = true`.
323 const crt_file = try sub_compilation.toCrtFile();324 const crt_file = try sub_compilation.toCrtFile();
324 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);325 try comp.queuePrelinkTaskMode(crt_file.full_object_path, true, &config);
325 assert(comp.tsan_lib == null);326 assert(comp.tsan_lib == null);
326 comp.tsan_lib = crt_file;327 comp.tsan_lib = crt_file;
327}328}
src/libs/libunwind.zig+1-1
...@@ -179,7 +179,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr...@@ -179,7 +179,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr
179 };179 };
180180
181 const crt_file = try sub_compilation.toCrtFile();181 const crt_file = try sub_compilation.toCrtFile();
182 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);182 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
183 assert(comp.libunwind_static_lib == null);183 assert(comp.libunwind_static_lib == null);
184 comp.libunwind_static_lib = crt_file;184 comp.libunwind_static_lib = crt_file;
185}185}
src/libs/musl.zig+1-1
...@@ -279,7 +279,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro...@@ -279,7 +279,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro
279 errdefer comp.gpa.free(basename);279 errdefer comp.gpa.free(basename);
280280
281 const crt_file = try sub_compilation.toCrtFile();281 const crt_file = try sub_compilation.toCrtFile();
282 try comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);282 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
283 {283 {
284 comp.mutex.lockUncancelable(io);284 comp.mutex.lockUncancelable(io);
285 defer comp.mutex.unlock(io);285 defer comp.mutex.unlock(io);
src/link.zig+31-26
...@@ -1105,27 +1105,29 @@ pub const File = struct {...@@ -1105,27 +1105,29 @@ pub const File = struct {
1105 }1105 }
11061106
1107 /// Opens a path as a static library and parses it into the linker.1107 /// Opens a path as a static library and parses it into the linker.
1108 /// If `query` is non-null, allows GNU ld scripts.1108 fn openLoadArchive(base: *File, path: Path, must_link: bool) anyerror!void {
1109 fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void {
1110 if (base.tag == .lld) return;1109 if (base.tag == .lld) return;
1111 const io = base.comp.io;1110 const io = base.comp.io;
1112 if (opt_query) |query| {1111 const archive = try openObject(io, path, must_link, false);
1113 const archive = try openObject(io, path, query.must_link, query.hidden);1112 errdefer archive.file.close(io);
1114 errdefer archive.file.close(io);1113 try loadInput(base, .{ .archive = archive });
1115 loadInput(base, .{ .archive = archive }) catch |err| switch (err) {1114 }
1116 error.BadMagic, error.UnexpectedEndOfFile => {1115
1117 if (base.tag != .elf and base.tag != .elf2) return err;1116 /// Opens a path as a static library and parses it into the linker. Allows GNU ld scripts.
1118 try loadGnuLdScript(base, path, query, archive.file);1117 fn openLoadArchiveQuery(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void {
1119 archive.file.close(io);1118 if (base.tag == .lld) return;
1120 return;1119 const io = base.comp.io;
1121 },1120 const archive = try openObject(io, path, query.must_link, query.hidden);
1122 else => return err,1121 errdefer archive.file.close(io);
1123 };1122 loadInput(base, .{ .archive = archive }) catch |err| switch (err) {
1124 } else {1123 error.BadMagic, error.UnexpectedEndOfFile => {
1125 const archive = try openObject(io, path, false, false);1124 if (base.tag != .elf and base.tag != .elf2) return err;
1126 errdefer archive.file.close(io);1125 try loadGnuLdScript(base, path, query, archive.file);
1127 try loadInput(base, .{ .archive = archive });1126 archive.file.close(io);
1128 }1127 return;
1128 },
1129 else => return err,
1130 };
1129 }1131 }
11301132
1131 /// Opens a path as a shared library and parses it into the linker.1133 /// Opens a path as a shared library and parses it into the linker.
...@@ -1180,7 +1182,7 @@ pub const File = struct {...@@ -1180,7 +1182,7 @@ pub const File = struct {
1180 switch (Compilation.classifyFileExt(arg.path)) {1182 switch (Compilation.classifyFileExt(arg.path)) {
1181 .shared_library => try openLoadDso(base, new_path, query),1183 .shared_library => try openLoadDso(base, new_path, query),
1182 .object => try openLoadObject(base, new_path),1184 .object => try openLoadObject(base, new_path),
1183 .static_library => try openLoadArchive(base, new_path, query),1185 .static_library => try openLoadArchiveQuery(base, new_path, query),
1184 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}),1186 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}),
1185 }1187 }
1186 } else {1188 } else {
...@@ -1380,7 +1382,10 @@ pub const PrelinkTask = union(enum) {...@@ -1380,7 +1382,10 @@ pub const PrelinkTask = union(enum) {
1380 /// Tells the linker to load an object file by path.1382 /// Tells the linker to load an object file by path.
1381 load_object: Path,1383 load_object: Path,
1382 /// Tells the linker to load a static library by path.1384 /// Tells the linker to load a static library by path.
1383 load_archive: Path,1385 load_archive: struct {
1386 path: Path,
1387 must_link: bool,
1388 },
1384 /// Tells the linker to load a shared library, possibly one that is a1389 /// Tells the linker to load a shared library, possibly one that is a
1385 /// GNU ld script.1390 /// GNU ld script.
1386 load_dso: Path,1391 load_dso: Path,
...@@ -1462,7 +1467,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1462,7 +1467,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1462 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),1467 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1463 }) catch return diags.setAllocFailure(),1468 }) catch return diags.setAllocFailure(),
1464 );1469 );
1465 base.openLoadArchive(archive_path, .{1470 base.openLoadArchiveQuery(archive_path, .{
1466 .preferred_mode = .dynamic,1471 .preferred_mode = .dynamic,
1467 .search_strategy = .paths_first,1472 .search_strategy = .paths_first,
1468 }) catch |archive_err| switch (archive_err) {1473 }) catch |archive_err| switch (archive_err) {
...@@ -1481,7 +1486,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1481,7 +1486,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1481 }) catch return diags.setAllocFailure(),1486 }) catch return diags.setAllocFailure(),
1482 );1487 );
1483 // glibc sometimes makes even archive files GNU ld scripts.1488 // glibc sometimes makes even archive files GNU ld scripts.
1484 base.openLoadArchive(path, .{1489 base.openLoadArchiveQuery(path, .{
1485 .preferred_mode = .static,1490 .preferred_mode = .static,
1486 .search_strategy = .no_fallback,1491 .search_strategy = .no_fallback,
1487 }) catch |err| switch (err) {1492 }) catch |err| switch (err) {
...@@ -1500,12 +1505,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {...@@ -1500,12 +1505,12 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1500 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),1505 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1501 };1506 };
1502 },1507 },
1503 .load_archive => |path| {1508 .load_archive => |load_archive| {
1504 const prog_node = comp.link_prog_node.start("Parse Archive", 0);1509 const prog_node = comp.link_prog_node.start("Parse Archive", 0);
1505 defer prog_node.end();1510 defer prog_node.end();
1506 base.openLoadArchive(path, null) catch |err| switch (err) {1511 base.openLoadArchive(load_archive.path, load_archive.must_link) catch |err| switch (err) {
1507 error.LinkFailure => return, // error reported via link_diags1512 error.LinkFailure => return, // error reported via link_diags
1508 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),1513 else => |e| diags.addParseError(load_archive.path, "failed to parse archive: {s}", .{@errorName(e)}),
1509 };1514 };
1510 },1515 },
1511 .load_dso => |path| {1516 .load_dso => |path| {
src/link/Lld.zig+11-7
...@@ -1086,10 +1086,10 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1086,10 +1086,10 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1086 .dso => continue,1086 .dso => continue,
1087 .object, .archive => |obj| {1087 .object, .archive => |obj| {
1088 if (obj.must_link and !whole_archive) {1088 if (obj.must_link and !whole_archive) {
1089 try argv.append("-whole-archive");1089 try argv.append("--whole-archive");
1090 whole_archive = true;1090 whole_archive = true;
1091 } else if (!obj.must_link and whole_archive) {1091 } else if (!obj.must_link and whole_archive) {
1092 try argv.append("-no-whole-archive");1092 try argv.append("--no-whole-archive");
1093 whole_archive = false;1093 whole_archive = false;
1094 }1094 }
1095 try argv.append(try obj.path.toString(arena));1095 try argv.append(try obj.path.toString(arena));
...@@ -1101,7 +1101,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1101,7 +1101,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
1101 };1101 };
11021102
1103 if (whole_archive) {1103 if (whole_archive) {
1104 try argv.append("-no-whole-archive");1104 try argv.append("--no-whole-archive");
1105 whole_archive = false;1105 whole_archive = false;
1106 }1106 }
11071107
...@@ -1115,7 +1115,11 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -1115,7 +1115,11 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
11151115
1116 if (comp.tsan_lib) |lib| {1116 if (comp.tsan_lib) |lib| {
1117 assert(comp.config.any_sanitize_thread);1117 assert(comp.config.any_sanitize_thread);
1118 try argv.append(try lib.full_object_path.toString(arena));1118 try argv.appendSlice(&.{
1119 "--whole-archive",
1120 try lib.full_object_path.toString(arena),
1121 "--no-whole-archive",
1122 });
1119 }1123 }
11201124
1121 if (comp.fuzzer_lib) |lib| {1125 if (comp.fuzzer_lib) |lib| {
...@@ -1549,10 +1553,10 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1549,10 +1553,10 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1549 for (comp.link_inputs) |link_input| switch (link_input) {1553 for (comp.link_inputs) |link_input| switch (link_input) {
1550 .object, .archive => |obj| {1554 .object, .archive => |obj| {
1551 if (obj.must_link and !whole_archive) {1555 if (obj.must_link and !whole_archive) {
1552 try argv.append("-whole-archive");1556 try argv.append("--whole-archive");
1553 whole_archive = true;1557 whole_archive = true;
1554 } else if (!obj.must_link and whole_archive) {1558 } else if (!obj.must_link and whole_archive) {
1555 try argv.append("-no-whole-archive");1559 try argv.append("--no-whole-archive");
1556 whole_archive = false;1560 whole_archive = false;
1557 }1561 }
1558 try argv.append(try obj.path.toString(arena));1562 try argv.append(try obj.path.toString(arena));
...@@ -1564,7 +1568,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1564,7 +1568,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1564 .res => unreachable,1568 .res => unreachable,
1565 };1569 };
1566 if (whole_archive) {1570 if (whole_archive) {
1567 try argv.append("-no-whole-archive");1571 try argv.append("--no-whole-archive");
1568 whole_archive = false;1572 whole_archive = false;
1569 }1573 }
15701574