| author | |
| committer | |
| log | 3cf8f283d3f271b6d0b5de39f55e4ea40b83eece |
| tree | 473271314571f59d4e0c0154acf078d1445b5fdb |
| parent | 4fc2acdaa4f2b649b17ddf958d2608abc4787a4e |
| parent | 58540f968a2ae53b4b1ff5a917fdb404088a222a |
| signature |
Dynamically link `libc++` if integrating with system LLVM6 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 | }; | ||
| 576 | 580 | ||
| 577 | // System -lc++ must be used because in this code path we are attempting to link | 581 | // 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 | |||
| 1917 | pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx; | ||
| 1918 | extern 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 | } |
| 1596 | 1605 | ||
| 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 | } |
| 176 | 176 | ||
| 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++ | ||
| 179 | fn 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 | |||
| 177 | pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | 188 | pub 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 | ; |
| 4489 | 4504 | ||
| 4505 | extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool; | ||
| 4506 | |||
| 4490 | extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | 4507 | extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 4491 | extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | 4508 | extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 4492 | 4509 |
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 | ||
| 3448 | bool 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 |
| 1418 | ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *); | 1418 | ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *); |
| 1419 | 1419 | ||
| 1420 | ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *); | 1420 | ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *); |
| 1421 | ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx(); | ||
| 1421 | #endif | 1422 | #endif |