authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-07-11 18:46:24-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-07-28 22:21:24-07:00
logb0525344a2b0b158369251c031159bef241048da
treeff088aec78d7471d1d2f6c92ee5a05ec9f2439fb
parent0fc79d602bf9b3a5c97cfc28b59193b005692cb2

Add check to verify libc++ is shared by LLVM/Clang

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
1917pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
1918extern 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}
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);
...@@ -4481,6 +4496,8 @@ pub const info_zen =...@@ -4481,6 +4496,8 @@ pub const info_zen =
4481 \\4496 \\
4482;4497;
44834498
4499extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
4500
4484extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4501extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
4485extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;4502extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44864503
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