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(
19131913 errors_len: *usize,
19141914 resources_path: [*:0]const u8,
19151915) ?*ASTUnit;
1916
1917pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
1918extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
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);
......@@ -4481,6 +4496,8 @@ pub const info_zen =
44814496 \\
44824497;
44834498
4499extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
4500
44844501extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44854502extern "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
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