diff --git a/src/Compilation.zig b/src/Compilation.zig index 343b22d2b1..b72a58f7fc 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -185,9 +185,9 @@ libcxxabi_static_lib: ?CRTFile = null, /// Populated when we build the libunwind static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). libunwind_static_lib: ?CRTFile = null, -/// Populated when we build the TSAN static library. A Job to build this is placed in the queue +/// Populated when we build the TSAN library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -tsan_static_lib: ?CRTFile = null, +tsan_lib: ?CRTFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). libc_static_lib: ?CRTFile = null, diff --git a/src/libtsan.zig b/src/libtsan.zig index 1aa32e6ff0..42b605bf3c 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -25,10 +25,19 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const root_name = "tsan"; - const output_mode = .Lib; - const link_mode = .static; const target = comp.getTarget(); + const root_name = switch (target.os.tag) { + // On Apple platforms, we use the same name as LLVM because the + // TSAN library implementation hard-codes a check for these names. + .macos => "clang_rt.tsan_osx_dynamic", + .ios => switch (target.abi) { + .simulator => "clang_rt.tsan_iossim_dynamic", + else => "clang_rt.tsan_ios_dynamic", + }, + else => "tsan", + }; + const link_mode: std.builtin.LinkMode = if (target.isDarwin()) .dynamic else .static; + const output_mode = .Lib; const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, @@ -43,6 +52,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); + const link_libcpp = target.isDarwin(); const config = Compilation.Config.resolve(.{ .output_mode = output_mode, @@ -54,6 +64,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .root_optimize_mode = optimize_mode, .root_strip = strip, .link_libc = true, + .link_libcpp = link_libcpp, }) catch |err| { comp.setMiscFailure( .libtsan, @@ -272,6 +283,14 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo }); } + const skip_linker_dependencies = !target.isDarwin(); + const linker_allow_shlib_undefined = target.isDarwin(); + const install_name = if (target.isDarwin()) + try std.fmt.allocPrintZ(arena, "@rpath/{s}", .{basename}) + else + null; + // Workaround for https://github.com/llvm/llvm-project/issues/97627 + const headerpad_size: ?u32 = if (target.isDarwin()) 32 else null; const sub_compilation = Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, @@ -294,7 +313,10 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, - .skip_linker_dependencies = true, + .skip_linker_dependencies = skip_linker_dependencies, + .linker_allow_shlib_undefined = linker_allow_shlib_undefined, + .install_name = install_name, + .headerpad_size = headerpad_size, }) catch |err| { comp.setMiscFailure( .libtsan, @@ -317,8 +339,8 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo }, }; - assert(comp.tsan_static_lib == null); - comp.tsan_static_lib = try sub_compilation.toCrtFile(); + assert(comp.tsan_lib == null); + comp.tsan_lib = try sub_compilation.toCrtFile(); } const tsan_sources = [_][]const u8{ diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5a14a544e8..b1048dfe9d 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1145,7 +1145,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: std.Progress.Node) l // TSAN if (comp.config.any_sanitize_thread) { - try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path }); + try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path }); } // libc @@ -1603,7 +1603,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - try argv.append(comp.tsan_static_lib.?.full_object_path); + try argv.append(comp.tsan_lib.?.full_object_path); } // libc @@ -2610,7 +2610,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: std.Progress.Node) !void } if (comp.config.any_sanitize_thread) { - try argv.append(comp.tsan_static_lib.?.full_object_path); + try argv.append(comp.tsan_lib.?.full_object_path); } // libc diff --git a/src/link/MachO.zig b/src/link/MachO.zig index dd185fcaec..4dcc11ef53 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -192,7 +192,7 @@ pub fn createEmpty( null else try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); - const allow_shlib_undefined = options.allow_shlib_undefined orelse comp.config.any_sanitize_thread; + const allow_shlib_undefined = options.allow_shlib_undefined orelse false; const self = try arena.create(MachO); self.* = .{ @@ -413,7 +413,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: std.Progress.Node) // TSAN if (comp.config.any_sanitize_thread) { - try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path }); + try positionals.append(.{ .path = comp.tsan_lib.?.full_object_path }); } for (positionals.items) |obj| { @@ -831,7 +831,9 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - try argv.append(comp.tsan_static_lib.?.full_object_path); + const path = comp.tsan_lib.?.full_object_path; + try argv.append(path); + try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." }); } for (self.lib_dirs) |lib_dir| { @@ -2959,7 +2961,8 @@ pub fn writeStrtab(self: *MachO, off: u32) !u32 { } fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { - const gpa = self.base.comp.gpa; + const comp = self.base.comp; + const gpa = comp.gpa; const needed_size = try load_commands.calcLoadCommandsSize(self, false); const buffer = try gpa.alloc(u8, needed_size); defer gpa.free(buffer); @@ -3015,8 +3018,16 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { ncmds += 1; } - try load_commands.writeRpathLCs(self.base.rpath_list, writer); - ncmds += self.base.rpath_list.len; + for (self.base.rpath_list) |rpath| { + try load_commands.writeRpathLC(rpath, writer); + ncmds += 1; + } + if (comp.config.any_sanitize_thread) { + const path = comp.tsan_lib.?.full_object_path; + const rpath = std.fs.path.dirname(path) orelse "."; + try load_commands.writeRpathLC(rpath, writer); + ncmds += 1; + } try writer.writeStruct(macho.source_version_command{ .version = 0 }); ncmds += 1; diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 394253db48..74d0c58cbd 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -18,6 +18,9 @@ fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool } pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + var sizeofcmds: u64 = 0; // LC_SEGMENT_64 @@ -48,7 +51,6 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 } // LC_ID_DYLIB if (macho_file.base.isDynLib()) { - const gpa = macho_file.base.comp.gpa; const emit = macho_file.base.emit; const install_name = macho_file.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); @@ -68,6 +70,16 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 assume_max_path_len, ); } + + if (comp.config.any_sanitize_thread) { + const path = comp.tsan_lib.?.full_object_path; + const rpath = std.fs.path.dirname(path) orelse "."; + sizeofcmds += calcInstallNameLen( + @sizeOf(macho.rpath_command), + rpath, + assume_max_path_len, + ); + } } // LC_SOURCE_VERSION sizeofcmds += @sizeOf(macho.source_version_command); @@ -245,24 +257,22 @@ pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { }, writer); } -pub fn writeRpathLCs(rpaths: []const []const u8, writer: anytype) !void { - for (rpaths) |rpath| { - const rpath_len = rpath.len + 1; - const cmdsize = @as(u32, @intCast(mem.alignForward( - u64, - @sizeOf(macho.rpath_command) + rpath_len, - @sizeOf(u64), - ))); - try writer.writeStruct(macho.rpath_command{ - .cmdsize = cmdsize, - .path = @sizeOf(macho.rpath_command), - }); - try writer.writeAll(rpath); - try writer.writeByte(0); - const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len; - if (padding > 0) { - try writer.writeByteNTimes(0, padding); - } +pub fn writeRpathLC(rpath: []const u8, writer: anytype) !void { + const rpath_len = rpath.len + 1; + const cmdsize = @as(u32, @intCast(mem.alignForward( + u64, + @sizeOf(macho.rpath_command) + rpath_len, + @sizeOf(u64), + ))); + try writer.writeStruct(macho.rpath_command{ + .cmdsize = cmdsize, + .path = @sizeOf(macho.rpath_command), + }); + try writer.writeAll(rpath); + try writer.writeByte(0); + const padding = cmdsize - @sizeOf(macho.rpath_command) - rpath_len; + if (padding > 0) { + try writer.writeByteNTimes(0, padding); } }