authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-29 14:18:32-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-29 14:18:32-07:00
log3cf8f283d3f271b6d0b5de39f55e4ea40b83eece
tree473271314571f59d4e0c0154acf078d1445b5fdb
parent4fc2acdaa4f2b649b17ddf958d2608abc4787a4e
parent58540f968a2ae53b4b1ff5a917fdb404088a222a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12085 from topolarity/dyn-link-libcpp

Dynamically link `libc++` if integrating with system LLVM

6 files changed, 69 insertions(+), 7 deletions(-)

build.zig+10-6
......@@ -573,13 +573,17 @@ fn addCmakeCfgOptionsToExe(
573573 exe.linkLibCpp();
574574 } else {
575575 const need_cpp_includes = true;
576 const lib_suffix = switch (cfg.llvm_linkage) {
577 .static => exe.target.staticLibSuffix()[1..],
578 .dynamic => exe.target.dynamicLibSuffix()[1..],
579 };
576580
577581 // System -lc++ must be used because in this code path we are attempting to link
578582 // against system-provided LLVM, Clang, LLD.
579583 if (exe.target.getOsTag() == .linux) {
580 // First we try to static link against gcc libstdc++. If that doesn't work,
581 // we fall back to -lc++ and cross our fingers.
582 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
584 // First we try to link against gcc libstdc++. If that doesn't work, we fall
585 // back to -lc++ and cross our fingers.
586 addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), "", need_cpp_includes) catch |err| switch (err) {
583587 error.RequiredLibraryNotFound => {
584588 exe.linkSystemLibrary("c++");
585589 },
......@@ -587,11 +591,11 @@ fn addCmakeCfgOptionsToExe(
587591 };
588592 exe.linkSystemLibrary("unwind");
589593 } else if (exe.target.isFreeBSD()) {
590 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
594 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
591595 exe.linkSystemLibrary("pthread");
592596 } else if (exe.target.getOsTag() == .openbsd) {
593 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
594 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
597 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
598 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
595599 } else if (exe.target.isDarwin()) {
596600 exe.linkSystemLibrary("c++");
597601 }
src/clang.zig+3
......@@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
19131913 errors_len: *usize,
19141914 resources_path: [*:0]const u8,
19151915) ?*ASTUnit;
1916
1917pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
1918extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
src/link/Elf.zig+9
......@@ -1592,6 +1592,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
15921592 }
15931593 }
15941594 }
1595 for (self.base.options.objects) |obj| {
1596 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1597 const lib_dir_path = std.fs.path.dirname(obj.path).?;
1598 if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
1599 try argv.append("-rpath");
1600 try argv.append(lib_dir_path);
1601 }
1602 }
1603 }
15951604 }
15961605
15971606 for (self.base.options.lib_dirs) |lib_dir| {
src/main.zig+18-1
......@@ -174,6 +174,17 @@ pub fn main() anyerror!void {
174174 return mainArgs(gpa, arena, args);
175175}
176176
177/// Check that LLVM and Clang have been linked properly so that they are using the same
178/// libc++ and can safely share objects with pointers to static variables in libc++
179fn verifyLibcxxCorrectlyLinked() void {
180 if (build_options.have_llvm and ZigClangIsLLVMUsingSeparateLibcxx()) {
181 fatal(
182 \\Zig was built/linked incorrectly: LLVM and Clang have separate copies of libc++
183 \\ If you are dynamically linking LLVM, make sure you dynamically link libc++ too
184 , .{});
185 }
186}
187
177188pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
178189 if (args.len <= 1) {
179190 std.log.info("{s}", .{usage});
......@@ -261,8 +272,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
261272 const stdout = io.getStdOut().writer();
262273 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
263274 } else if (mem.eql(u8, cmd, "version")) {
264 return std.io.getStdOut().writeAll(build_options.version ++ "\n");
275 try std.io.getStdOut().writeAll(build_options.version ++ "\n");
276 // Check libc++ linkage to make sure Zig was built correctly, but only for "env" and "version"
277 // to avoid affecting the startup time for build-critical commands (check takes about ~10 μs)
278 return verifyLibcxxCorrectlyLinked();
265279 } else if (mem.eql(u8, cmd, "env")) {
280 verifyLibcxxCorrectlyLinked();
266281 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
267282 } else if (mem.eql(u8, cmd, "zen")) {
268283 return io.getStdOut().writeAll(info_zen);
......@@ -4487,6 +4502,8 @@ pub const info_zen =
44874502 \\
44884503;
44894504
4505extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
4506
44904507extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44914508extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44924509
src/zig_clang.cpp+28
......@@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi
34323432 const llvm::APSInt *result = &casted->getInitVal();
34333433 return reinterpret_cast<const ZigClangAPSInt *>(result);
34343434}
3435
3436// Get a pointer to a static variable in libc++ from LLVM and make sure that
3437// it matches our own.
3438//
3439// This check is needed because if static/dynamic linking is mixed incorrectly,
3440// it's possible for Clang and LLVM to end up with duplicate "copies" of libc++.
3441//
3442// This is not benign: Static variables are not shared, so equality comparisons
3443// that depend on pointers to static variables will fail. One such failure is
3444// std::generic_category(), which causes POSIX error codes to compare as unequal
3445// when passed between LLVM and Clang.
3446//
3447// See also: https://github.com/ziglang/zig/issues/11168
3448bool ZigClangIsLLVMUsingSeparateLibcxx() {
3449
3450 // Temporarily create an InMemoryFileSystem, so that we can perform a file
3451 // lookup that is guaranteed to fail.
3452 auto FS = new llvm::vfs::InMemoryFileSystem(true);
3453 auto StatusOrErr = FS->status("foo.txt");
3454 delete FS;
3455
3456 // This should return a POSIX (generic_category) error code, but if LLVM has
3457 // its own copy of libc++ this will actually be a separate category instance.
3458 assert(!StatusOrErr);
3459 auto EC = StatusOrErr.getError();
3460 return EC.category() != std::generic_category();
3461}
3462
src/zig_clang.h+1
......@@ -1418,4 +1418,5 @@ ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const
14181418ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);
14191419
14201420ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
1421ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx();
14211422#endif