| author | |
| committer | |
| log | b0525344a2b0b158369251c031159bef241048da |
| tree | ff088aec78d7471d1d2f6c92ee5a05ec9f2439fb |
| parent | 0fc79d602bf9b3a5c97cfc28b59193b005692cb2 |
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.
I believe this is the cause of https://github.com/ziglang/zig/issues/11168
In order to avoid affecting build times when Zig is repeatedly invoked,
we only enable this check for "zig env" and "zig version"4 files changed, 50 insertions(+), 1 deletions(-)
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/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); |
| ... | @@ -4481,6 +4496,8 @@ pub const info_zen = | ... | @@ -4481,6 +4496,8 @@ pub const info_zen = |
| 4481 | \\ | 4496 | \\ |
| 4482 | ; | 4497 | ; |
| 4483 | 4498 | ||
| 4499 | extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool; | ||
| 4500 | |||
| 4484 | extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | 4501 | extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 4485 | extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | 4502 | extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 4486 | 4503 |
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 |