Skip to content

Commit cecd96b

Browse files
committed
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 #11168
1 parent 8b3f15f commit cecd96b

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

src/clang.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
19131913
errors_len: *usize,
19141914
resources_path: [*:0]const u8,
19151915
) ?*ASTUnit;
1916+
1917+
pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
1918+
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;

src/main.zig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ pub fn main() anyerror!void {
175175
}
176176

177177
pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
178+
if (build_options.have_llvm) {
179+
if (ZigClangIsLLVMUsingSeparateLibcxx()) {
180+
fatal(
181+
\\Zig was built/linked incorrectly: LLVM and Clang have separate copies of libc++
182+
\\ If you are dynamically linking LLVM, make sure you dynamically link libc++ too
183+
, .{});
184+
}
185+
}
186+
178187
if (args.len <= 1) {
179188
std.log.info("{s}", .{usage});
180189
fatal("expected command argument", .{});
@@ -4457,6 +4466,8 @@ pub const info_zen =
44574466
\\
44584467
;
44594468

4469+
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
4470+
44604471
extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44614472
extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
44624473

src/zig_clang.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,4 +1418,5 @@ ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const
14181418
ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);
14191419

14201420
ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
1421+
ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx();
14211422
#endif

0 commit comments

Comments
 (0)