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(...@@ -573,13 +573,17 @@ fn addCmakeCfgOptionsToExe(
573 exe.linkLibCpp();573 exe.linkLibCpp();
574 } else {574 } else {
575 const need_cpp_includes = true;575 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
577 // System -lc++ must be used because in this code path we are attempting to link581 // System -lc++ must be used because in this code path we are attempting to link
578 // against system-provided LLVM, Clang, LLD.582 // against system-provided LLVM, Clang, LLD.
579 if (exe.target.getOsTag() == .linux) {583 if (exe.target.getOsTag() == .linux) {
580 // First we try to static link against gcc libstdc++. If that doesn't work,584 // First we try to link against gcc libstdc++. If that doesn't work, we fall
581 // we fall back to -lc++ and cross our fingers.585 // back to -lc++ and cross our fingers.
582 addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {586 addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), "", need_cpp_includes) catch |err| switch (err) {
583 error.RequiredLibraryNotFound => {587 error.RequiredLibraryNotFound => {
584 exe.linkSystemLibrary("c++");588 exe.linkSystemLibrary("c++");
585 },589 },
...@@ -587,11 +591,11 @@ fn addCmakeCfgOptionsToExe(...@@ -587,11 +591,11 @@ fn addCmakeCfgOptionsToExe(
587 };591 };
588 exe.linkSystemLibrary("unwind");592 exe.linkSystemLibrary("unwind");
589 } else if (exe.target.isFreeBSD()) {593 } 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);
591 exe.linkSystemLibrary("pthread");595 exe.linkSystemLibrary("pthread");
592 } else if (exe.target.getOsTag() == .openbsd) {596 } else if (exe.target.getOsTag() == .openbsd) {
593 try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);597 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
594 try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);598 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
595 } else if (exe.target.isDarwin()) {599 } else if (exe.target.isDarwin()) {
596 exe.linkSystemLibrary("c++");600 exe.linkSystemLibrary("c++");
597 }601 }
src/clang.zig+3
...@@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(...@@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
1913 errors_len: *usize,1913 errors_len: *usize,
1914 resources_path: [*:0]const u8,1914 resources_path: [*:0]const u8,
1915) ?*ASTUnit;1915) ?*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...@@ -1592,6 +1592,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
1592 }1592 }
1593 }1593 }
1594 }1594 }
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 }
1595 }1604 }
15961605
1597 for (self.base.options.lib_dirs) |lib_dir| {1606 for (self.base.options.lib_dirs) |lib_dir| {
src/main.zig+18-1
...@@ -174,6 +174,17 @@ pub fn main() anyerror!void {...@@ -174,6 +174,17 @@ pub fn main() anyerror!void {
174 return mainArgs(gpa, arena, args);174 return mainArgs(gpa, arena, args);
175}175}
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
177pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {188pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
178 if (args.len <= 1) {189 if (args.len <= 1) {
179 std.log.info("{s}", .{usage});190 std.log.info("{s}", .{usage});
...@@ -261,8 +272,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -261,8 +272,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
261 const stdout = io.getStdOut().writer();272 const stdout = io.getStdOut().writer();
262 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);273 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
263 } else if (mem.eql(u8, cmd, "version")) {274 } 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();
265 } else if (mem.eql(u8, cmd, "env")) {279 } else if (mem.eql(u8, cmd, "env")) {
280 verifyLibcxxCorrectlyLinked();
266 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());281 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
267 } else if (mem.eql(u8, cmd, "zen")) {282 } else if (mem.eql(u8, cmd, "zen")) {
268 return io.getStdOut().writeAll(info_zen);283 return io.getStdOut().writeAll(info_zen);
...@@ -4487,6 +4502,8 @@ pub const info_zen =...@@ -4487,6 +4502,8 @@ pub const info_zen =
4487 \\4502 \\
4488;4503;
44894504
4505extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
4506
4490extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4507extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
4491extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4508extern "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...@@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi
3432 const llvm::APSInt *result = &casted->getInitVal();3432 const llvm::APSInt *result = &casted->getInitVal();
3433 return reinterpret_cast<const ZigClangAPSInt *>(result);3433 return reinterpret_cast<const ZigClangAPSInt *>(result);
3434}3434}
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...@@ -1418,4 +1418,5 @@ ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const
1418ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);1418ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);
14191419
1420ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);1420ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
1421ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx();
1421#endif1422#endif