-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[clang] Add builtin_get_vtable_pointer and virtual_member_address #135469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang Author: Oliver Hunt (ojhunt) ChangesThese are a pair of builtins to support particularly weird edge case operations while correctly handling the non-trivial implicit pointer authentication schemas applied to polymorphic members. Patch is 39.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135469.diff 9 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 3b8a9cac6587a..643dea3c6cbf7 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3031,6 +3031,39 @@ following way:
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
+``__builtin_get_vtable_pointer``
+--------------------------------
+
+``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
+pointer from an instance of a polymorphic C++ class.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_get_vtable_pointer(PolymorphicClass*)
+
+**Example of Use**:
+
+.. code-block:: c++
+
+ struct PolymorphicClass {
+ virtual ~PolymorphicClass();
+ };
+
+ PolymorphicClass anInstance;
+ const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
+
+**Description**:
+
+The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
+pointer from a polymorphic C++ type. If the target platform authenticates
+vtable pointers, this builtin will perform the authentication and produce
+the underlying raw pointer. The object being queried must be polymorphic,
+and so must also be a complete type.
+
+Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
+
``__builtin_call_with_static_chain``
------------------------------------
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 868e5b92acdc9..a55f411343c2d 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -970,6 +970,18 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
let Prototype = "bool(void*)";
}
+def GetVtablePointer : LangBuiltin<"CXX_LANG"> {
+ let Spellings = ["__builtin_get_vtable_pointer"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*)";
+}
+
+def VirtualMemberAddress : Builtin {
+ let Spellings = ["__builtin_virtual_member_address"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*,void*)";
+}
+
// GCC exception builtins
def EHReturn : Builtin {
let Spellings = ["__builtin_eh_return"];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 180ca39bc07e9..8c534850696d9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1013,6 +1013,13 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
"%select{subtraction|addition}0 of address-of-label expressions is not "
"supported with ptrauth indirect gotos">;
+def err_virtual_member_lhs_cxxrec : Error<
+ "first argument to __builtin_virtual_member_address must have C++ class type">;
+def err_virtual_member_addrof : Error<
+ "second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'">;
+def err_virtual_member_inherit : Error<
+ "first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -12547,6 +12554,14 @@ def err_bit_cast_non_trivially_copyable : Error<
def err_bit_cast_type_size_mismatch : Error<
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
+
+def err_get_vtable_pointer_incorrect_type : Error<
+ "__builtin_get_vtable_pointer requires an argument of%select{| polymorphic}0 class pointer type"
+ ", but %1 %select{was provided|has no virtual methods}0"
+>;
+def err_get_vtable_pointer_requires_complete_type : Error<
+ "__builtin_get_vtable_pointer requires an argument with a complete type, but %0 is incomplete">;
+
// SYCL-specific diagnostics
def warn_sycl_kernel_num_of_template_params : Warning<
"'sycl_kernel' attribute only applies to a function template with at least"
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index fe55dfffc1cbe..316f5debf89d7 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -17,6 +17,7 @@
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
#include "CGOpenCLRuntime.h"
+#include "CGPointerAuthInfo.h"
#include "CGRecordLayout.h"
#include "CGValue.h"
#include "CodeGenFunction.h"
@@ -5349,6 +5350,35 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Result);
}
+ case Builtin::BI__builtin_virtual_member_address: {
+ Address This = EmitLValue(E->getArg(0)).getAddress();
+ APValue ConstMemFun;
+ E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr);
+ const CXXMethodDecl *CXXMethod =
+ cast<CXXMethodDecl>(ConstMemFun.getMemberPointerDecl());
+ const CGFunctionInfo &FInfo =
+ CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod);
+ llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(FInfo);
+ CGCallee VCallee = CGM.getCXXABI().getVirtualFunctionPointer(
+ *this, CXXMethod, This, Ty, E->getBeginLoc());
+ llvm::Value *Callee = VCallee.getFunctionPointer();
+ if (const CGPointerAuthInfo &Schema = VCallee.getPointerAuthInfo())
+ Callee = EmitPointerAuthAuth(Schema, Callee);
+ return RValue::get(Callee);
+ }
+
+ case Builtin::BI__builtin_get_vtable_pointer: {
+ auto target = E->getArg(0);
+ auto type = target->getType();
+ auto decl = type->getPointeeCXXRecordDecl();
+ assert(decl);
+ auto thisAddress = EmitPointerWithAlignment(target);
+ assert(thisAddress.isValid());
+ auto vtablePointer =
+ GetVTablePtr(thisAddress, Int8PtrTy, decl, VTableAuthMode::MustTrap);
+ return RValue::get(vtablePointer);
+ }
+
case Builtin::BI__exception_code:
case Builtin::BI_exception_code:
return RValue::get(EmitSEHExceptionCode());
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index bffd0dd461d3d..77b8b304f7f0a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1764,6 +1764,57 @@ static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
return Call;
}
+static ExprResult VirtualMemberAddress(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 2))
+ return ExprError();
+
+ for (int i = 0; i < 2; ++i) {
+ auto ArgRValue = S.DefaultFunctionArrayLvalueConversion(Call->getArg(1));
+ if (ArgRValue.isInvalid())
+ return ExprError();
+
+ auto Arg = ArgRValue.get();
+ Call->setArg(1, Arg);
+ }
+
+ if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent())
+ return Call;
+
+ auto ThisArg = Call->getArg(0);
+ auto ThisTy = ThisArg->getType();
+ if (!ThisTy->getAsCXXRecordDecl()) {
+ S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec);
+ return ExprError();
+ }
+
+ auto MemFunArg = Call->getArg(1);
+ APValue Result;
+ if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ if (!Result.isMemberPointer() ||
+ !isa<CXXMethodDecl>(Result.getMemberPointerDecl())) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ auto CXXMethod = cast<CXXMethodDecl>(Result.getMemberPointerDecl());
+ if (!CXXMethod->isVirtual()) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() &&
+ !S.IsDerivedFrom(Call->getBeginLoc(), ThisTy,
+ CXXMethod->getFunctionObjectParameterType())) {
+ S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit);
+ return ExprError();
+ }
+ return Call;
+}
+
static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
if (checkPointerAuthEnabled(S, Call))
return ExprError();
@@ -1782,6 +1833,39 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
return Call;
}
+static ExprResult GetVTablePointer(Sema &S, CallExpr *call) {
+ if (S.checkArgCount(call, 1))
+ return ExprError();
+ auto rvalue = S.DefaultFunctionArrayLvalueConversion(call->getArg(0));
+ if (rvalue.isInvalid())
+ return ExprError();
+ call->setArg(0, rvalue.get());
+ auto expression = call->getArg(0);
+ QualType expressionType = expression->getType();
+ const CXXRecordDecl *objectType = expressionType->getPointeeCXXRecordDecl();
+ if (!expressionType->isPointerType() || !objectType) {
+ S.Diag(expression->getBeginLoc(),
+ diag::err_get_vtable_pointer_incorrect_type)
+ << 0 << expressionType;
+ return ExprError();
+ }
+ if (S.RequireCompleteType(
+ expression->getBeginLoc(), expressionType->getPointeeType(),
+ diag::err_get_vtable_pointer_requires_complete_type)) {
+ return ExprError();
+ }
+
+ if (!objectType->isPolymorphic()) {
+ S.Diag(expression->getBeginLoc(),
+ diag::err_get_vtable_pointer_incorrect_type)
+ << 1 << objectType;
+ return ExprError();
+ }
+ QualType returnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
+ call->setType(returnType);
+ return call;
+}
+
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
if (S.checkArgCount(TheCall, 1))
return ExprError();
@@ -2625,6 +2709,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return PointerAuthAuthAndResign(*this, TheCall);
case Builtin::BI__builtin_ptrauth_string_discriminator:
return PointerAuthStringDiscriminator(*this, TheCall);
+
+ case Builtin::BI__builtin_get_vtable_pointer:
+ return GetVTablePointer(*this, TheCall);
+ case Builtin::BI__builtin_virtual_member_address:
+ return VirtualMemberAddress(*this, TheCall);
+
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
diff --git a/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
new file mode 100644
index 0000000000000..5577e01a09f6f
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
@@ -0,0 +1,304 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-NOAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-TYPEAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-ADDRESSAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-BOTHAUTH %s
+// FIXME: Assume load should not require -fstrict-vtable-pointers
+
+namespace test1 {
+struct A {
+ A();
+ virtual void bar();
+};
+
+struct B : A {
+ B();
+ virtual void foo();
+};
+
+struct Z : A {};
+struct C : Z, B {
+ C();
+ virtual void wibble();
+};
+
+struct D : virtual A {
+};
+
+struct E : D, B {
+};
+
+const void *a(A *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b(B *o) {
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ // CHECK-NOAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b_as_A(B *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16b_as_AEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((A *)o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11cEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_Z(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_ZEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((Z *)o);
+ // CHECK-NOAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_B(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_BEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((B *)o);
+ // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8
+ // CHECK-NOAUTH: br label %cast.end
+ // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %cast.result to i64
+ // CHECK-ADDRESSAUTH: %3 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 2, i64 %2)
+ // CHECK-ADDRESSAUTH: %5 = inttoptr i64 %4 to ptr
+ // CHECK-ADDRESSAUTH: %6 = load volatile i8, ptr %5, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %2, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d(D *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11dEPNS_1DE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %1 = ptrtoint ptr %0 to i64
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d_as_A(D *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16d_as_AEPNS_1DE(ptr %o...
[truncated]
|
@llvm/pr-subscribers-clang-codegen Author: Oliver Hunt (ojhunt) ChangesThese are a pair of builtins to support particularly weird edge case operations while correctly handling the non-trivial implicit pointer authentication schemas applied to polymorphic members. Patch is 39.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135469.diff 9 Files Affected:
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 3b8a9cac6587a..643dea3c6cbf7 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3031,6 +3031,39 @@ following way:
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
+``__builtin_get_vtable_pointer``
+--------------------------------
+
+``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
+pointer from an instance of a polymorphic C++ class.
+
+**Syntax**:
+
+.. code-block:: c++
+
+ __builtin_get_vtable_pointer(PolymorphicClass*)
+
+**Example of Use**:
+
+.. code-block:: c++
+
+ struct PolymorphicClass {
+ virtual ~PolymorphicClass();
+ };
+
+ PolymorphicClass anInstance;
+ const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
+
+**Description**:
+
+The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
+pointer from a polymorphic C++ type. If the target platform authenticates
+vtable pointers, this builtin will perform the authentication and produce
+the underlying raw pointer. The object being queried must be polymorphic,
+and so must also be a complete type.
+
+Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
+
``__builtin_call_with_static_chain``
------------------------------------
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 868e5b92acdc9..a55f411343c2d 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -970,6 +970,18 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
let Prototype = "bool(void*)";
}
+def GetVtablePointer : LangBuiltin<"CXX_LANG"> {
+ let Spellings = ["__builtin_get_vtable_pointer"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*)";
+}
+
+def VirtualMemberAddress : Builtin {
+ let Spellings = ["__builtin_virtual_member_address"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const];
+ let Prototype = "void*(void*,void*)";
+}
+
// GCC exception builtins
def EHReturn : Builtin {
let Spellings = ["__builtin_eh_return"];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 180ca39bc07e9..8c534850696d9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1013,6 +1013,13 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
"%select{subtraction|addition}0 of address-of-label expressions is not "
"supported with ptrauth indirect gotos">;
+def err_virtual_member_lhs_cxxrec : Error<
+ "first argument to __builtin_virtual_member_address must have C++ class type">;
+def err_virtual_member_addrof : Error<
+ "second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'">;
+def err_virtual_member_inherit : Error<
+ "first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -12547,6 +12554,14 @@ def err_bit_cast_non_trivially_copyable : Error<
def err_bit_cast_type_size_mismatch : Error<
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
+
+def err_get_vtable_pointer_incorrect_type : Error<
+ "__builtin_get_vtable_pointer requires an argument of%select{| polymorphic}0 class pointer type"
+ ", but %1 %select{was provided|has no virtual methods}0"
+>;
+def err_get_vtable_pointer_requires_complete_type : Error<
+ "__builtin_get_vtable_pointer requires an argument with a complete type, but %0 is incomplete">;
+
// SYCL-specific diagnostics
def warn_sycl_kernel_num_of_template_params : Warning<
"'sycl_kernel' attribute only applies to a function template with at least"
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index fe55dfffc1cbe..316f5debf89d7 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -17,6 +17,7 @@
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
#include "CGOpenCLRuntime.h"
+#include "CGPointerAuthInfo.h"
#include "CGRecordLayout.h"
#include "CGValue.h"
#include "CodeGenFunction.h"
@@ -5349,6 +5350,35 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Result);
}
+ case Builtin::BI__builtin_virtual_member_address: {
+ Address This = EmitLValue(E->getArg(0)).getAddress();
+ APValue ConstMemFun;
+ E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr);
+ const CXXMethodDecl *CXXMethod =
+ cast<CXXMethodDecl>(ConstMemFun.getMemberPointerDecl());
+ const CGFunctionInfo &FInfo =
+ CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod);
+ llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(FInfo);
+ CGCallee VCallee = CGM.getCXXABI().getVirtualFunctionPointer(
+ *this, CXXMethod, This, Ty, E->getBeginLoc());
+ llvm::Value *Callee = VCallee.getFunctionPointer();
+ if (const CGPointerAuthInfo &Schema = VCallee.getPointerAuthInfo())
+ Callee = EmitPointerAuthAuth(Schema, Callee);
+ return RValue::get(Callee);
+ }
+
+ case Builtin::BI__builtin_get_vtable_pointer: {
+ auto target = E->getArg(0);
+ auto type = target->getType();
+ auto decl = type->getPointeeCXXRecordDecl();
+ assert(decl);
+ auto thisAddress = EmitPointerWithAlignment(target);
+ assert(thisAddress.isValid());
+ auto vtablePointer =
+ GetVTablePtr(thisAddress, Int8PtrTy, decl, VTableAuthMode::MustTrap);
+ return RValue::get(vtablePointer);
+ }
+
case Builtin::BI__exception_code:
case Builtin::BI_exception_code:
return RValue::get(EmitSEHExceptionCode());
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index bffd0dd461d3d..77b8b304f7f0a 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1764,6 +1764,57 @@ static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
return Call;
}
+static ExprResult VirtualMemberAddress(Sema &S, CallExpr *Call) {
+ if (S.checkArgCount(Call, 2))
+ return ExprError();
+
+ for (int i = 0; i < 2; ++i) {
+ auto ArgRValue = S.DefaultFunctionArrayLvalueConversion(Call->getArg(1));
+ if (ArgRValue.isInvalid())
+ return ExprError();
+
+ auto Arg = ArgRValue.get();
+ Call->setArg(1, Arg);
+ }
+
+ if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent())
+ return Call;
+
+ auto ThisArg = Call->getArg(0);
+ auto ThisTy = ThisArg->getType();
+ if (!ThisTy->getAsCXXRecordDecl()) {
+ S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec);
+ return ExprError();
+ }
+
+ auto MemFunArg = Call->getArg(1);
+ APValue Result;
+ if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ if (!Result.isMemberPointer() ||
+ !isa<CXXMethodDecl>(Result.getMemberPointerDecl())) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ auto CXXMethod = cast<CXXMethodDecl>(Result.getMemberPointerDecl());
+ if (!CXXMethod->isVirtual()) {
+ S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
+ return ExprError();
+ }
+
+ if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() &&
+ !S.IsDerivedFrom(Call->getBeginLoc(), ThisTy,
+ CXXMethod->getFunctionObjectParameterType())) {
+ S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit);
+ return ExprError();
+ }
+ return Call;
+}
+
static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
if (checkPointerAuthEnabled(S, Call))
return ExprError();
@@ -1782,6 +1833,39 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
return Call;
}
+static ExprResult GetVTablePointer(Sema &S, CallExpr *call) {
+ if (S.checkArgCount(call, 1))
+ return ExprError();
+ auto rvalue = S.DefaultFunctionArrayLvalueConversion(call->getArg(0));
+ if (rvalue.isInvalid())
+ return ExprError();
+ call->setArg(0, rvalue.get());
+ auto expression = call->getArg(0);
+ QualType expressionType = expression->getType();
+ const CXXRecordDecl *objectType = expressionType->getPointeeCXXRecordDecl();
+ if (!expressionType->isPointerType() || !objectType) {
+ S.Diag(expression->getBeginLoc(),
+ diag::err_get_vtable_pointer_incorrect_type)
+ << 0 << expressionType;
+ return ExprError();
+ }
+ if (S.RequireCompleteType(
+ expression->getBeginLoc(), expressionType->getPointeeType(),
+ diag::err_get_vtable_pointer_requires_complete_type)) {
+ return ExprError();
+ }
+
+ if (!objectType->isPolymorphic()) {
+ S.Diag(expression->getBeginLoc(),
+ diag::err_get_vtable_pointer_incorrect_type)
+ << 1 << objectType;
+ return ExprError();
+ }
+ QualType returnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
+ call->setType(returnType);
+ return call;
+}
+
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
if (S.checkArgCount(TheCall, 1))
return ExprError();
@@ -2625,6 +2709,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return PointerAuthAuthAndResign(*this, TheCall);
case Builtin::BI__builtin_ptrauth_string_discriminator:
return PointerAuthStringDiscriminator(*this, TheCall);
+
+ case Builtin::BI__builtin_get_vtable_pointer:
+ return GetVTablePointer(*this, TheCall);
+ case Builtin::BI__builtin_virtual_member_address:
+ return VirtualMemberAddress(*this, TheCall);
+
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
diff --git a/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
new file mode 100644
index 0000000000000..5577e01a09f6f
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-get-vtable-pointer.cpp
@@ -0,0 +1,304 @@
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-NOAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-TYPEAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-ADDRESSAUTH %s
+// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -fptrauth-vtable-pointer-address-discrimination -emit-llvm -O1 -disable-llvm-passes -no-enable-noundef-analysis -o - | FileCheck --check-prefix=CHECK-BOTHAUTH %s
+// FIXME: Assume load should not require -fstrict-vtable-pointers
+
+namespace test1 {
+struct A {
+ A();
+ virtual void bar();
+};
+
+struct B : A {
+ B();
+ virtual void foo();
+};
+
+struct Z : A {};
+struct C : Z, B {
+ C();
+ virtual void wibble();
+};
+
+struct D : virtual A {
+};
+
+struct E : D, B {
+};
+
+const void *a(A *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11aEPNS_1AE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b(B *o) {
+ // CHECK-TYPEAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ // CHECK-NOAUTH: define ptr @_ZN5test11bEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *b_as_A(B *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16b_as_AEPNS_1BE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((A *)o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11cEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_Z(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_ZEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((Z *)o);
+ // CHECK-NOAUTH: %0 = load ptr, ptr %o.addr, align 8
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %1, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *c_as_B(C *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16c_as_BEPNS_1CE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer((B *)o);
+ // CHECK-NOAUTH: %add.ptr = getelementptr inbounds i8, ptr %0, i64 8
+ // CHECK-NOAUTH: br label %cast.end
+ // CHECK-NOAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %cast.result, align 8
+ // CHECK-TYPEAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-TYPEAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %cast.result to i64
+ // CHECK-ADDRESSAUTH: %3 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 2, i64 %2)
+ // CHECK-ADDRESSAUTH: %5 = inttoptr i64 %4 to ptr
+ // CHECK-ADDRESSAUTH: %6 = load volatile i8, ptr %5, align 8
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 %2, i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d(D *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test11dEPNS_1DE(ptr %o) #0 {
+ return __builtin_get_vtable_pointer(o);
+ // CHECK-NOAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %vtable = load ptr, ptr %0, align 8
+ // CHECK-TYPEAUTH: %1 = ptrtoint ptr %vtable to i64
+ // CHECK-TYPEAUTH: %2 = call i64 @llvm.ptrauth.auth(i64 %1, i32 2, i64 48388)
+ // CHECK-TYPEAUTH: %3 = inttoptr i64 %2 to ptr
+ // CHECK-TYPEAUTH: %4 = load volatile i8, ptr %3, align 8
+ // CHECK-ADDRESSAUTH: %1 = ptrtoint ptr %0 to i64
+ // CHECK-ADDRESSAUTH: %2 = ptrtoint ptr %vtable to i64
+ // CHECK-ADDRESSAUTH: %3 = call i64 @llvm.ptrauth.auth(i64 %2, i32 2, i64 %1)
+ // CHECK-ADDRESSAUTH: %4 = inttoptr i64 %3 to ptr
+ // CHECK-ADDRESSAUTH: %5 = load volatile i8, ptr %4, align 8
+ // CHECK-BOTHAUTH: [[T1:%.*]] = ptrtoint ptr %0 to i64
+ // CHECK-BOTHAUTH: [[T2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T1]], i64 48388)
+ // CHECK-BOTHAUTH: [[T3:%.*]] = ptrtoint ptr %vtable to i64
+ // CHECK-BOTHAUTH: [[T4:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T3]], i32 2, i64 [[T2]])
+ // CHECK-BOTHAUTH: [[T5:%.*]] = inttoptr i64 [[T4]] to ptr
+ // CHECK-BOTHAUTH: [[T6:%.*]] = load volatile i8, ptr [[T5]], align 8
+}
+
+const void *d_as_A(D *o) {
+ // CHECK-NOAUTH: define ptr @_ZN5test16d_as_AEPNS_1DE(ptr %o...
[truncated]
|
@@ -0,0 +1,41 @@ | |||
// RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-calls -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes CHECK,CHECK-AUTH | |||
// RUN: %clang_cc1 -triple arm64-apple-ios -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes CHECK,CHECK-NOAUTH |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will you please also add ELF target triples in tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing, what is the triple you folk use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asl :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From discord: aarch64-linux-pauthtest
I just noticed there's no documentation for Due to code drift and time I realized that they disagree as to whether they should take |
3c20ae3
to
e03f703
Compare
These are a pair of builtins to support particularly weird edge case operations while correctly handling the non-trivial implicit pointer authentication schemas applied to polymorphic members. Co-authored-by: Tim Northover
e03f703
to
1607a76
Compare
most of our builtins take pointers. Accepting both seems unwise. |
Hmmm, I'll revert to only supporting pointers in |
@AaronBallman Given we need to keep the reference argument to __virtual_member_address, would you like to support both, or remain as reference only? |
I'm confused -- how is there existing code depending on a builtin which doesn't yet exist in Clang? Are we copying these builtins from somewhere else? |
This is part of the pointer authentication support we are upstreaming, all of which has been in use for a long time, just not available in upstream llvm/clang. |
Oof. On the one hand, we don't want to break your existing users. On the other hand, we don't want to force the much broader audience of users into a worse interface. Are those uses in your downstream able to use things like fix-its to switch from the reference-based interface to a pointer-based interface? (Basically, is there a transition approach that leads to a better world?) |
case Builtin::BI__builtin_virtual_member_address: { | ||
Address This = EmitLValue(E->getArg(0)).getAddress(); | ||
APValue ConstMemFun; | ||
E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we don't care about the return value of this call? I guess b/c we verify in VirtualMemberAddress
, so we are using this for a side effect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the isCXX11ConstantExpr gives us the target through ConstMemFun, but I'll see if there's a cleaner way to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expr::EvaluateAsLValue
?
if (RecordType->isPointerOrReferenceType()) | ||
RecordType = RecordType->getPointeeType(); | ||
const CXXRecordDecl *Decl = RecordType->getAsCXXRecordDecl(); | ||
assert(Decl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note you are asserting pointers here but not in BI__builtin_virtual_member_address
case, not obvious to me why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Different authors over time, I'll unify on not asserting as null deref is the downstream outcome.
I sent this in discord rather than in GH where the reply can be seen by others and not lost to time:
|
pending the above changes I'll remove the review flag so people aren't confused |
Call->setArg(0, ThisArg.get()); | ||
const Expr *Subject = Call->getArg(0); | ||
QualType SubjectType = Subject->getType(); | ||
if (SubjectType->isPointerOrReferenceType()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be reverting this to require a pointer arg
|
||
const Expr *ThisArg = Call->getArg(0); | ||
QualType ThisTy = ThisArg->getType(); | ||
if (ThisTy->isPointerOrReferenceType()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update this to make it warn on a reference parameter
These are a pair of builtins to support particularly weird edge case operations while correctly handling the non-trivial implicit pointer authentication schemas applied to polymorphic members.
Co-authored-by: Tim Northover