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,15 @@ return __builtin_addressof(value); } +``__builtin_function_start`` +----------------------------- + +``__builtin_function_start`` returns the address of a function body. Depending +on the platform and enabled compiler features, the returned address may +be different than the normally taken function address. For example, with +``-fsanitize=cfi``, the compiler replaces function addresses with references to +the CFI jump table, which may not be desirable in low-level system code. + ``__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 @@ -3106,6 +3106,10 @@ return getUnusedResultAttr(Ctx) != nullptr; } + /// Returns a ValueDecl of the first call argument, or a nullptr if one + /// doesn't exist. + const ValueDecl *getArgDecl(ASTContext &Ctx) const; + SourceLocation getRParenLoc() const { return RParenLoc; } void setRParenLoc(SourceLocation L) { RParenLoc = L; } 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 @@ -1573,6 +1573,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 @@ -1567,6 +1567,22 @@ return end; } +const ValueDecl *CallExpr::getArgDecl(ASTContext &Context) const { + if (getNumArgs() < 1) + return nullptr; + + const Expr *E = getArg(0)->IgnoreParenCasts(); + + if (const UnaryOperator *UnaryOp = dyn_cast_or_null(E)) + if (UnaryOp->getOpcode() == UnaryOperator::Opcode::UO_AddrOf) + E = UnaryOp->getSubExpr(); + + if (E->getStmtClass() == Stmt::DeclRefExprClass) + return cast(E)->getDecl(); + + return nullptr; +} + OffsetOfExpr *OffsetOfExpr::Create(const ASTContext &C, QualType type, SourceLocation OperatorLoc, TypeSourceInfo *tsi, 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 @@ -2163,6 +2163,10 @@ } } + if (auto *Call = dyn_cast_or_null(BaseE)) + if (Call->getBuiltinCallee() == Builtin::BI__builtin_function_start) + return true; + // Check that the object is a global. Note that the fake 'this' object we // manufacture when checking potential constant expressions is conservatively // assumed to be global here. @@ -8982,6 +8986,9 @@ switch (BuiltinOp) { case Builtin::BI__builtin_addressof: return evaluateLValue(E->getArg(0), Result); + case Builtin::BI__builtin_function_start: + Result.set(E); + return true; case Builtin::BI__builtin_assume_aligned: { // We need to be very careful here because: if the pointer does not have the // asserted alignment, then the behavior is undefined, and undefined 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 @@ -4477,6 +4477,8 @@ } 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)); 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,8 @@ ConstantLValue ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { unsigned builtin = E->getBuiltinCallee(); + if (builtin == Builtin::BI__builtin_function_start) + return CGM.GetFunctionStart(E); 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 CallExpr *Call); + /// 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 @@ -3770,6 +3770,18 @@ return F; } +llvm::Constant *CodeGenModule::GetFunctionStart(const CallExpr *C) { + const ValueDecl *Decl = C->getArgDecl(Context); + + assert(isa_and_nonnull(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,23 @@ 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; + + const FunctionDecl *FD = + dyn_cast_or_null(TheCall->getArgDecl(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) { @@ -1874,6 +1891,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,51 @@ +// 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) {} +void b(void) {} +void c(void *p) {} + +// CHECK: @e = global i8* bitcast (void ()* no_cfi @_Z1av to i8*) +const void *e = __builtin_function_start(a); +// CHECK: @f = global [2 x i8*] [i8* bitcast (void ()* @_Z1bv to i8*), i8* bitcast (void ()* no_cfi @_Z1bv to i8*)] +void *f[] = {(void *)b, __builtin_function_start(b)}; + +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 d(void) { + // CHECK: store i8* bitcast (void ()* no_cfi @_Z1bv to i8*), i8** %g + void *g = __builtin_function_start(b); + // 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}}