Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -565,25 +565,29 @@ fn addCmakeCfgOptionsToExe(
exe.linkLibCpp();
} else {
const need_cpp_includes = true;
const lib_suffix = switch (cfg.llvm_linkage) {
.static => exe.target.staticLibSuffix()[1..],
.dynamic => exe.target.dynamicLibSuffix()[1..],
};

// System -lc++ must be used because in this code path we are attempting to link
// against system-provided LLVM, Clang, LLD.
if (exe.target.getOsTag() == .linux) {
// First we try to static link against gcc libstdc++. If that doesn't work,
// we fall back to -lc++ and cross our fingers.
addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
// First we try to link against gcc libstdc++. If that doesn't work, we fall
// back to -lc++ and cross our fingers.
addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), "", need_cpp_includes) catch |err| switch (err) {
error.RequiredLibraryNotFound => {
exe.linkSystemLibrary("c++");
},
else => |e| return e,
};
exe.linkSystemLibrary("unwind");
} else if (exe.target.isFreeBSD()) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
exe.linkSystemLibrary("pthread");
} else if (exe.target.getOsTag() == .openbsd) {
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
} else if (exe.target.isDarwin()) {
exe.linkSystemLibrary("c++");
}
Expand Down
3 changes: 3 additions & 0 deletions src/clang.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
errors_len: *usize,
resources_path: [*:0]const u8,
) ?*ASTUnit;

pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
9 changes: 9 additions & 0 deletions src/link/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
}
}
}
for (self.base.options.objects) |obj| {
if (Compilation.classifyFileExt(obj.path) == .shared_library) {
const lib_dir_path = std.fs.path.dirname(obj.path).?;
if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
try argv.append("-rpath");
try argv.append(lib_dir_path);
}
}
}
}

for (self.base.options.lib_dirs) |lib_dir| {
Expand Down
19 changes: 18 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ pub fn main() anyerror!void {
return mainArgs(gpa, arena, args);
}

/// Check that LLVM and Clang have been linked properly so that they are using the same
/// libc++ and can safely share objects with pointers to static variables in libc++
fn verifyLibcxxCorrectlyLinked() void {
if (build_options.have_llvm and ZigClangIsLLVMUsingSeparateLibcxx()) {
fatal(
\\Zig was built/linked incorrectly: LLVM and Clang have separate copies of libc++
\\ If you are dynamically linking LLVM, make sure you dynamically link libc++ too
, .{});
}
}

pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
if (args.len <= 1) {
std.log.info("{s}", .{usage});
Expand Down Expand Up @@ -261,8 +272,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
const stdout = io.getStdOut().writer();
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
} else if (mem.eql(u8, cmd, "version")) {
return std.io.getStdOut().writeAll(build_options.version ++ "\n");
try std.io.getStdOut().writeAll(build_options.version ++ "\n");
// Check libc++ linkage to make sure Zig was built correctly, but only for "env" and "version"
// to avoid affecting the startup time for build-critical commands (check takes about ~10 μs)
return verifyLibcxxCorrectlyLinked();
} else if (mem.eql(u8, cmd, "env")) {
verifyLibcxxCorrectlyLinked();
return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
} else if (mem.eql(u8, cmd, "zen")) {
return io.getStdOut().writeAll(info_zen);
Expand Down Expand Up @@ -4481,6 +4496,8 @@ pub const info_zen =
\\
;

extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;

extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;

Expand Down
28 changes: 28 additions & 0 deletions src/zig_clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi
const llvm::APSInt *result = &casted->getInitVal();
return reinterpret_cast<const ZigClangAPSInt *>(result);
}

// Get a pointer to a static variable in libc++ from LLVM and make sure that
// it matches our own.
//
// This check is needed because if static/dynamic linking is mixed incorrectly,
// it's possible for Clang and LLVM to end up with duplicate "copies" of libc++.
//
// This is not benign: Static variables are not shared, so equality comparisons
// that depend on pointers to static variables will fail. One such failure is
// std::generic_category(), which causes POSIX error codes to compare as unequal
// when passed between LLVM and Clang.
//
// See also: https://github.com/ziglang/zig/issues/11168
bool ZigClangIsLLVMUsingSeparateLibcxx() {

// Temporarily create an InMemoryFileSystem, so that we can perform a file
// lookup that is guaranteed to fail.
auto FS = new llvm::vfs::InMemoryFileSystem(true);
auto StatusOrErr = FS->status("foo.txt");
delete FS;

// This should return a POSIX (generic_category) error code, but if LLVM has
// its own copy of libc++ this will actually be a separate category instance.
assert(!StatusOrErr);
auto EC = StatusOrErr.getError();
return EC.category() != std::generic_category();
}

1 change: 1 addition & 0 deletions src/zig_clang.h
Original file line number Diff line number Diff line change
Expand Up @@ -1418,4 +1418,5 @@ ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const
ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);

ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx();
#endif