[CodeGen] Emit a CoreFoundation link guard when @available is used
After r297760, __isOSVersionAtLeast in compiler-rt loads the CoreFoundation symbols at runtime. This means that `@available` will always fail when used in a binary without a linked CoreFoundation. This commit forces Clang to emit a reference to a CoreFoundation symbol when `@available` is used to ensure that linking will fail when CoreFoundation isn't linked with the build product. rdar://31039592 Differential Revision: https://reviews.llvm.org/D30977 llvm-svn: 298588
This commit is contained in:
parent
5ffe4e14f1
commit
a8fbef44fe
|
@ -3416,4 +3416,37 @@ CodeGenFunction::EmitBuiltinAvailable(ArrayRef<llvm::Value *> Args) {
|
|||
return Builder.CreateICmpNE(CallRes, llvm::Constant::getNullValue(Int32Ty));
|
||||
}
|
||||
|
||||
void CodeGenModule::emitAtAvailableLinkGuard() {
|
||||
if (!IsOSVersionAtLeastFn)
|
||||
return;
|
||||
// @available requires CoreFoundation only on Darwin.
|
||||
if (!Target.getTriple().isOSDarwin())
|
||||
return;
|
||||
// Add -framework CoreFoundation to the linker commands. We still want to
|
||||
// emit the core foundation reference down below because otherwise if
|
||||
// CoreFoundation is not used in the code, the linker won't link the
|
||||
// framework.
|
||||
auto &Context = getLLVMContext();
|
||||
llvm::Metadata *Args[2] = {llvm::MDString::get(Context, "-framework"),
|
||||
llvm::MDString::get(Context, "CoreFoundation")};
|
||||
LinkerOptionsMetadata.push_back(llvm::MDNode::get(Context, Args));
|
||||
// Emit a reference to a symbol from CoreFoundation to ensure that
|
||||
// CoreFoundation is linked into the final binary.
|
||||
llvm::FunctionType *FTy =
|
||||
llvm::FunctionType::get(Int32Ty, {VoidPtrTy}, false);
|
||||
llvm::Constant *CFFunc =
|
||||
CreateRuntimeFunction(FTy, "CFBundleGetVersionNumber");
|
||||
|
||||
llvm::FunctionType *CheckFTy = llvm::FunctionType::get(VoidTy, {}, false);
|
||||
llvm::Function *CFLinkCheckFunc = cast<llvm::Function>(CreateBuiltinFunction(
|
||||
CheckFTy, "__clang_at_available_requires_core_foundation_framework"));
|
||||
CFLinkCheckFunc->setLinkage(llvm::GlobalValue::LinkOnceAnyLinkage);
|
||||
CFLinkCheckFunc->setVisibility(llvm::GlobalValue::HiddenVisibility);
|
||||
CodeGenFunction CGF(*this);
|
||||
CGF.Builder.SetInsertPoint(CGF.createBasicBlock("", CFLinkCheckFunc));
|
||||
CGF.EmitNounwindRuntimeCall(CFFunc, llvm::Constant::getNullValue(VoidPtrTy));
|
||||
CGF.Builder.CreateUnreachable();
|
||||
addCompilerUsedGlobal(CFLinkCheckFunc);
|
||||
}
|
||||
|
||||
CGObjCRuntime::~CGObjCRuntime() {}
|
||||
|
|
|
@ -414,6 +414,7 @@ void CodeGenModule::Release() {
|
|||
CoverageMapping->emit();
|
||||
if (CodeGenOpts.SanitizeCfiCrossDso)
|
||||
CodeGenFunction(*this).EmitCfiCheckFail();
|
||||
emitAtAvailableLinkGuard();
|
||||
emitLLVMUsed();
|
||||
if (SanStats)
|
||||
SanStats->finish();
|
||||
|
|
|
@ -1286,6 +1286,10 @@ private:
|
|||
/// Emit any vtables which we deferred and still have a use for.
|
||||
void EmitDeferredVTables();
|
||||
|
||||
/// Emit a dummy function that reference a CoreFoundation symbol when
|
||||
/// @available is used on Darwin.
|
||||
void emitAtAvailableLinkGuard();
|
||||
|
||||
/// Emit the llvm.used and llvm.compiler.used metadata.
|
||||
void emitLLVMUsed();
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D USE_BUILTIN %s | FileCheck --check-prefixes=CHECK,CHECK_LINK_OPT %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -D DEF_CF %s | FileCheck --check-prefixes=CHECK_CF,CHECK_LINK_OPT %s
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK_NO_GUARD %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux -emit-llvm -o - %s | FileCheck --check-prefix=CHECK_NO_GUARD %s
|
||||
|
||||
#ifdef DEF_CF
|
||||
struct CFBundle;
|
||||
typedef struct CFBundle *CFBundleRef;
|
||||
unsigned CFBundleGetVersionNumber(CFBundleRef bundle);
|
||||
// CHECK_CF: declare i32 @CFBundleGetVersionNumber(%struct.CFBundle*)
|
||||
// CHECK_CF: @__clang_at_available_requires_core_foundation_framework
|
||||
// CHECK_CF-NEXT: call {{.*}}@CFBundleGetVersionNumber
|
||||
#endif
|
||||
|
||||
void use_at_available() {
|
||||
#ifdef DEF_CF
|
||||
CFBundleGetVersionNumber(0);
|
||||
#endif
|
||||
#ifdef USE_BUILTIN
|
||||
if (__builtin_available(macos 10.12, *))
|
||||
;
|
||||
#else
|
||||
if (@available(macos 10.12, *))
|
||||
;
|
||||
#endif
|
||||
}
|
||||
|
||||
// CHECK: @llvm.compiler.used{{.*}}@__clang_at_available_requires_core_foundation_framework
|
||||
|
||||
// CHECK: declare i32 @CFBundleGetVersionNumber(i8*)
|
||||
|
||||
// CHECK-LABEL: linkonce hidden void @__clang_at_available_requires_core_foundation_framework
|
||||
// CHECK: call i32 @CFBundleGetVersionNumber(i8* null)
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
// CHECK_NO_GUARD-NOT: __clang_at_available_requires_core_foundation_framework
|
||||
// CHECK_NO_GUARD-NOT: CFBundleGetVersionNumber
|
||||
|
||||
// CHECK_LINK_OPT: !"Linker Options", ![[OPTS:[0-9]+]]
|
||||
// CHECK_LINK_OPT: ![[OPTS]] = !{![[FRAMEWORK:[0-9]+]]
|
||||
// CHECK_LINK_OPT: ![[FRAMEWORK]] = !{!"-framework", !"CoreFoundation"}
|
||||
|
||||
// CHECK_NO_GUARD-NOT: "Linker Options"
|
||||
// CHECK_NO_GUARD-NOT: CoreFoundation
|
Loading…
Reference in New Issue