mirror of
https://github.com/ziglang/zig.git
synced 2024-11-26 23:22:44 +00:00
Merge pull request #12085 from topolarity/dyn-link-libcpp
Dynamically link `libc++` if integrating with system LLVM
This commit is contained in:
commit
3cf8f283d3
16
build.zig
16
build.zig
@ -573,13 +573,17 @@ 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++");
|
||||
},
|
||||
@ -587,11 +591,11 @@ fn addCmakeCfgOptionsToExe(
|
||||
};
|
||||
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++");
|
||||
}
|
||||
|
@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
|
||||
errors_len: *usize,
|
||||
resources_path: [*:0]const u8,
|
||||
) ?*ASTUnit;
|
||||
|
||||
pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
|
||||
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
|
||||
|
@ -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| {
|
||||
|
19
src/main.zig
19
src/main.zig
@ -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});
|
||||
@ -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);
|
||||
@ -4487,6 +4502,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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user