diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2517,6 +2517,48 @@ return __builtin_addressof(value); } +``__builtin_function_start`` +----------------------------- + +``__builtin_function_start`` returns the address of a function body. + +**Syntax**: + +.. code-block:: c++ + + void *__builtin_function_start(function) + +**Example of use**: + +.. code-block:: c++ + + void a() {} + void *p = __builtin_function_start(a); + + class A { + public: + void a(int n); + void a(); + }; + + void A::a(int n) {} + void A::a() {} + + void *pa1 = __builtin_function_start((void(A::*)(int)) &A::a); + void *pa2 = __builtin_function_start((void(A::*)()) &A::a); + +**Description**: + +The ``__builtin_function_start`` builtin accepts an argument that can be +constant-evaluated to a function, and returns the address of the function +body. This builtin is not supported on all targets. + +The returned pointer may differ from the normally taken function address +and is not safe to call. For example, with ``-fsanitize=cfi``, taking a +function address produces a callable pointer to a CFI jump table, while +``__builtin_function_start`` returns an address that fails +:doc:`cfi-icall` checks. + ``__builtin_operator_new`` and ``__builtin_operator_delete`` ------------------------------------------------------------ diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -572,6 +572,12 @@ bool isConstantInitializer(ASTContext &Ctx, bool ForRef, const Expr **Culprit = nullptr) const; + /// If this expression is an unambiguous reference to a single declaration, + /// in the style of __builtin_function_start, return that declaration. Note + /// that this may return a non-static member function or field in C++ if this + /// expression is a member pointer constant. + const ValueDecl *getAsBuiltinConstantDeclRef(const ASTContext &Context) const; + /// EvalStatus is a struct with detailed info about an evaluation in progress. struct EvalStatus { /// Whether the evaluated expression has side effects. diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1575,6 +1575,7 @@ // Clang builtins (not available in GCC). BUILTIN(__builtin_addressof, "v*v&", "nct") +BUILTIN(__builtin_function_start, "v*v&", "nct") BUILTIN(__builtin_operator_new, "v*z", "tc") BUILTIN(__builtin_operator_delete, "vv*", "tn") BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -838,6 +838,9 @@ "%2, but the corresponding specifier may require size %3">, InGroup; +def err_function_start_invalid_type: Error< + "argument must be a function">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -202,6 +202,23 @@ return false; } +const ValueDecl * +Expr::getAsBuiltinConstantDeclRef(const ASTContext &Context) const { + Expr::EvalResult Eval; + + if (EvaluateAsConstantExpr(Eval, Context)) { + APValue &Value = Eval.Val; + + if (Value.isMemberPointer()) + return Value.getMemberPointerDecl(); + + if (Value.isLValue() && Value.getLValueOffset().isZero()) + return Value.getLValueBase().dyn_cast(); + } + + return nullptr; +} + // Amusing macro metaprogramming hack: check whether a class provides // a more specific implementation of getExprLoc(). // diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1954,11 +1954,12 @@ return true; } -/// Should this call expression be treated as a string literal? -static bool IsStringLiteralCall(const CallExpr *E) { +/// Should this call expression be treated as a constant? +static bool IsConstantCall(const CallExpr *E) { unsigned Builtin = E->getBuiltinCallee(); return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || - Builtin == Builtin::BI__builtin___NSStringMakeConstantString); + Builtin == Builtin::BI__builtin___NSStringMakeConstantString || + Builtin == Builtin::BI__builtin_function_start); } static bool IsGlobalLValue(APValue::LValueBase B) { @@ -2004,7 +2005,7 @@ case Expr::ObjCBoxedExprClass: return cast(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: - return IsStringLiteralCall(cast(E)); + return IsConstantCall(cast(E)); // For GCC compatibility, &&label has static storage duration. case Expr::AddrLabelExprClass: return true; @@ -8967,7 +8968,7 @@ } bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) { - if (IsStringLiteralCall(E)) + if (IsConstantCall(E)) return Success(E); if (unsigned BuiltinOp = E->getBuiltinCallee()) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4505,6 +4505,9 @@ } case Builtin::BI__builtin_addressof: return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this)); + case Builtin::BI__builtin_function_start: + return RValue::get(CGM.GetFunctionStart( + E->getArg(0)->getAsBuiltinConstantDeclRef(CGM.getContext()))); case Builtin::BI__builtin_operator_new: return EmitBuiltinNewDeleteCall( E->getCallee()->getType()->castAs(), E, false); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1988,6 +1988,9 @@ ConstantLValue ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { unsigned builtin = E->getBuiltinCallee(); + if (builtin == Builtin::BI__builtin_function_start) + return CGM.GetFunctionStart( + E->getArg(0)->getAsBuiltinConstantDeclRef(CGM.getContext())); if (builtin != Builtin::BI__builtin___CFStringMakeConstantString && builtin != Builtin::BI__builtin___NSStringMakeConstantString) return nullptr; diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -881,6 +881,9 @@ ForDefinition_t IsForDefinition = NotForDefinition); + // Return the function body address of the given function. + llvm::Constant *GetFunctionStart(const ValueDecl *Decl); + /// Get the address of the RTTI descriptor for the given type. llvm::Constant *GetAddrOfRTTIDescriptor(QualType Ty, bool ForEH = false); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3886,6 +3886,14 @@ return F; } +llvm::Constant *CodeGenModule::GetFunctionStart(const ValueDecl *Decl) { + llvm::GlobalValue *F = + cast(GetAddrOfFunction(Decl)->stripPointerCasts()); + + return llvm::ConstantExpr::getBitCast(llvm::NoCFIValue::get(F), + llvm::Type::getInt8PtrTy(VMContext)); +} + static const FunctionDecl * GetRuntimeFunctionDecl(ASTContext &C, StringRef Name) { TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -195,6 +195,29 @@ return false; } +/// Check that the argument to __builtin_function_start is a function. +static bool SemaBuiltinFunctionStart(Sema &S, CallExpr *TheCall) { + if (checkArgCount(S, TheCall, 1)) + return true; + + ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0)); + if (Arg.isInvalid()) + return true; + + TheCall->setArg(0, Arg.get()); + const FunctionDecl *FD = dyn_cast_or_null( + Arg.get()->getAsBuiltinConstantDeclRef(S.getASTContext())); + + if (!FD) { + S.Diag(TheCall->getBeginLoc(), diag::err_function_start_invalid_type) + << TheCall->getSourceRange(); + return true; + } + + return !S.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + TheCall->getBeginLoc()); +} + /// Check the number of arguments and set the result type to /// the argument type. static bool SemaBuiltinPreserveAI(Sema &S, CallExpr *TheCall) { @@ -1918,6 +1941,10 @@ if (SemaBuiltinAddressof(*this, TheCall)) return ExprError(); break; + case Builtin::BI__builtin_function_start: + if (SemaBuiltinFunctionStart(*this, TheCall)) + return ExprError(); + break; case Builtin::BI__builtin_is_aligned: case Builtin::BI__builtin_align_up: case Builtin::BI__builtin_align_down: diff --git a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -66,7 +66,8 @@ case Builtin::BI__builtin_expect: case Builtin::BI__builtin_expect_with_probability: case Builtin::BI__builtin_assume_aligned: - case Builtin::BI__builtin_addressof: { + case Builtin::BI__builtin_addressof: + case Builtin::BI__builtin_function_start: { // For __builtin_unpredictable, __builtin_expect, // __builtin_expect_with_probability and __builtin_assume_aligned, // just return the value of the subexpression. diff --git a/clang/test/CodeGen/builtin-function-start.cpp b/clang/test/CodeGen/builtin-function-start.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtin-function-start.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=cfi-icall -o - %s | FileCheck %s + +#if !__has_builtin(__builtin_function_start) +#error "missing __builtin_function_start" +#endif + +void a(void) {} +// CHECK: @e = global i8* bitcast (void ()* no_cfi @_Z1av to i8*) +const void *e = __builtin_function_start(a); + +constexpr void (*d)() = &a; +// CHECK: @f = global i8* bitcast (void ()* no_cfi @_Z1av to i8*) +const void *f = __builtin_function_start(d); + +void b(void) {} +// CHECK: @g = global [2 x i8*] [i8* bitcast (void ()* @_Z1bv to i8*), i8* bitcast (void ()* no_cfi @_Z1bv to i8*)] +void *g[] = {(void *)b, __builtin_function_start(b)}; + +void c(void *p) {} + +class A { +public: + void f(); + virtual void g(); + static void h(); + int i() const; + int i(int n) const; +}; + +void A::f() {} +void A::g() {} +void A::h() {} + +// CHECK: define {{.*}}i32 @_ZNK1A1iEv(%class.A* {{.*}}%this) +int A::i() const { return 0; } + +// CHECK: define {{.*}}i32 @_ZNK1A1iEi(%class.A* {{.*}}%this, i32 %n) +int A::i(int n) const { return 0; } + +void h(void) { + // CHECK: store i8* bitcast (void ()* no_cfi @_Z1bv to i8*), i8** %g + void *g = __builtin_function_start(b); + // CHECK: call void @_Z1cPv(i8* bitcast (void ()* no_cfi @_Z1av to i8*)) + c(__builtin_function_start(a)); + + // CHECK: store i8* bitcast (void (%class.A*)* no_cfi @_ZN1A1fEv to i8*), i8** %Af + void *Af = __builtin_function_start(&A::f); + // CHECK: store i8* bitcast (void (%class.A*)* no_cfi @_ZN1A1gEv to i8*), i8** %Ag + void *Ag = __builtin_function_start(&A::g); + // CHECK: store i8* bitcast (void ()* no_cfi @_ZN1A1hEv to i8*), i8** %Ah + void *Ah = __builtin_function_start(&A::h); + // CHECK: store i8* bitcast (i32 (%class.A*)* no_cfi @_ZNK1A1iEv to i8*), i8** %Ai1 + void *Ai1 = __builtin_function_start((int(A::*)() const) & A::i); + // CHECK: store i8* bitcast (i32 (%class.A*, i32)* no_cfi @_ZNK1A1iEi to i8*), i8** %Ai2 + void *Ai2 = __builtin_function_start((int(A::*)(int) const) & A::i); +} diff --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp --- a/clang/test/SemaCXX/builtins.cpp +++ b/clang/test/SemaCXX/builtins.cpp @@ -39,6 +39,13 @@ S *ptmp = __builtin_addressof(S{}); // expected-error {{taking the address of a temporary}} } +namespace function_start { +void a(void) {} +int n; +void *p = __builtin_function_start(n); // expected-error {{argument must be a function}} +static_assert(__builtin_function_start(a) == a, ""); // expected-error {{static_assert expression is not an integral constant expression}} +} // namespace function_start + void no_ms_builtins() { __assume(1); // expected-error {{use of undeclared}} __noop(1); // expected-error {{use of undeclared}}