Skip to content

Commit cddbbd2

Browse files
committed
[clang] Add builtin_get_vtable_pointer and virtual_member_address
1 parent bf3b529 commit cddbbd2

File tree

9 files changed

+697
-0
lines changed

9 files changed

+697
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2741,6 +2741,40 @@ Query for this feature with ``__has_builtin(__builtin_readcyclecounter)``. Note
27412741
that even if present, its use may depend on run-time privilege or other OS
27422742
controlled state.
27432743
2744+
2745+
``__builtin_get_vtable_pointer``
2746+
------------------------------
2747+
2748+
``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
2749+
pointer from an instance of a polymorphic C++ class.
2750+
2751+
**Syntax**:
2752+
2753+
.. code-block:: c++
2754+
2755+
__builtin_get_vtable_pointer(PolymorphicClass*)
2756+
2757+
**Example of Use**:
2758+
2759+
.. code-block:: c++
2760+
2761+
struct PolymorphicClass {
2762+
virtual ~PolymorphicClass();
2763+
};
2764+
2765+
PolymorphicClass anInstance;
2766+
const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
2767+
2768+
**Description**:
2769+
2770+
The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
2771+
pointer from a polymorphic C++ type. If the target platform authenticates
2772+
vtable pointers, this builtin will perform the authentication and produce
2773+
the underlying raw pointer. The object being queried must be polymorphic,
2774+
and so must also be a complete type.
2775+
2776+
Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
2777+
27442778
``__builtin_dump_struct``
27452779
-------------------------
27462780

clang/include/clang/Basic/Builtins.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ BUILTIN(__builtin_thread_pointer, "v*", "nc")
631631
BUILTIN(__builtin_launder, "v*v*", "ntE")
632632
LANGBUILTIN(__builtin_is_constant_evaluated, "b", "nE", CXX_LANG)
633633

634+
LANGBUILTIN(__builtin_get_vtable_pointer, "v*v*", "tnc", CXX_LANG)
635+
634636
// GCC exception builtins
635637
BUILTIN(__builtin_eh_return, "vzv*", "r") // FIXME: Takes intptr_t, not size_t!
636638
BUILTIN(__builtin_frob_return_addr, "v*v*", "n")
@@ -1705,6 +1707,8 @@ BUILTIN(__builtin_ptrauth_auth_and_resign, "v*v*iv*iv*", "tn")
17051707
BUILTIN(__builtin_ptrauth_auth, "v*v*iv*", "tn")
17061708
BUILTIN(__builtin_ptrauth_string_discriminator, "zcC*", "ncE")
17071709

1710+
BUILTIN(__builtin_virtual_member_address, "v*v*v*", "tnc")
1711+
17081712
// OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions.
17091713
// We need the generic prototype, since the packet type could be anything.
17101714
LANGBUILTIN(read_pipe, "i.", "tn", OCL_PIPE)

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,13 @@ def err_ptrauth_extra_discriminator_invalid : Error<
956956
"extra discriminator for %select{__ptrauth|ptrauth_struct}2 must be between "
957957
"0 and %1; value is %0">;
958958

959+
def err_virtual_member_lhs_cxxrec : Error<
960+
"first argument to __builtin_virtual_member_address must have C++ class type">;
961+
def err_virtual_member_addrof : Error<
962+
"second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'">;
963+
def err_virtual_member_inherit : Error<
964+
"first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined">;
965+
959966
/// main()
960967
// static main() is not an error in C, just in C++.
961968
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -11926,6 +11933,14 @@ def err_bit_cast_non_trivially_copyable : Error<
1192611933
def err_bit_cast_type_size_mismatch : Error<
1192711934
"__builtin_bit_cast source size does not equal destination size (%0 vs %1)">;
1192811935

11936+
11937+
def err_get_vtable_pointer_incorrect_type : Error<
11938+
"__builtin_get_vtable_pointer requires an argument of%select{| polymorphic}0 class pointer type"
11939+
", but %1 %select{was provided|has no virtual methods}0"
11940+
>;
11941+
def err_get_vtable_pointer_requires_complete_type : Error<
11942+
"__builtin_get_vtable_pointer requires an argument with a complete type, but %0 is incomplete">;
11943+
1192911944
// SYCL-specific diagnostics
1193011945
def warn_sycl_kernel_num_of_template_params : Warning<
1193111946
"'sycl_kernel' attribute only applies to a function template with at least"

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4969,6 +4969,38 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
49694969
return RValue::get(result);
49704970
}
49714971

4972+
case Builtin::BI__builtin_virtual_member_address: {
4973+
Address This = EmitLValue(E->getArg(0)).getAddress();
4974+
APValue ConstMemFun;
4975+
E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr);
4976+
auto CXXMethod = cast<CXXMethodDecl>(ConstMemFun.getMemberPointerDecl());
4977+
const CGFunctionInfo &FInfo =
4978+
CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod);
4979+
auto Ty = CGM.getTypes().GetFunctionType(FInfo);
4980+
CGCallee VCallee = CGCallee::forVirtual(nullptr, CXXMethod, This, Ty);
4981+
auto Callee = VCallee.prepareConcreteCallee(*this).getFunctionPointer();
4982+
if (CGM.getCodeGenOpts().PointerAuth.FunctionPointers) {
4983+
auto Strip = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_strip);
4984+
Callee =
4985+
EmitRuntimeCall(Strip, {Builder.CreatePtrToInt(Callee, IntPtrTy),
4986+
llvm::ConstantInt::get(IntTy, 0)});
4987+
}
4988+
Callee = Builder.CreateBitOrPointerCast(Callee, Int8PtrTy);
4989+
return RValue::get(Callee);
4990+
}
4991+
4992+
case Builtin::BI__builtin_get_vtable_pointer: {
4993+
auto target = E->getArg(0);
4994+
auto type = target->getType();
4995+
auto decl = type->getPointeeCXXRecordDecl();
4996+
assert(decl);
4997+
auto thisAddress = EmitPointerWithAlignment(target);
4998+
assert(thisAddress.isValid());
4999+
auto vtablePointer =
5000+
GetVTablePtr(thisAddress, Int8PtrTy, decl, VTableAuthMode::MustTrap);
5001+
return RValue::get(vtablePointer);
5002+
}
5003+
49725004
case Builtin::BI__exception_code:
49735005
case Builtin::BI_exception_code:
49745006
return RValue::get(EmitSEHExceptionCode());

clang/lib/Sema/SemaChecking.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,6 +2238,57 @@ static ExprResult SemaPointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
22382238
return Call;
22392239
}
22402240

2241+
static ExprResult SemaVirtualMemberAddress(Sema &S, CallExpr *Call) {
2242+
if (checkArgCount(S, Call, 2)) return ExprError();
2243+
2244+
for (int i = 0; i < 2; ++i) {
2245+
auto ArgRValue = S.DefaultFunctionArrayLvalueConversion(Call->getArg(1));
2246+
if (ArgRValue.isInvalid())
2247+
return ExprError();
2248+
2249+
auto Arg = ArgRValue.get();
2250+
Call->setArg(1, Arg);
2251+
}
2252+
2253+
if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent())
2254+
return Call;
2255+
2256+
auto ThisArg = Call->getArg(0);
2257+
auto ThisTy = ThisArg->getType();
2258+
if (!ThisTy->getAsCXXRecordDecl()) {
2259+
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec);
2260+
return ExprError();
2261+
}
2262+
2263+
auto MemFunArg = Call->getArg(1);
2264+
APValue Result;
2265+
if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) {
2266+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
2267+
return ExprError();
2268+
}
2269+
2270+
if (!Result.isMemberPointer() ||
2271+
!isa<CXXMethodDecl>(Result.getMemberPointerDecl())) {
2272+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
2273+
return ExprError();
2274+
}
2275+
2276+
auto CXXMethod = cast<CXXMethodDecl>(Result.getMemberPointerDecl());
2277+
if (!CXXMethod->isVirtual()) {
2278+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
2279+
return ExprError();
2280+
}
2281+
2282+
if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() &&
2283+
!S.IsDerivedFrom(Call->getBeginLoc(), ThisTy,
2284+
CXXMethod->getThisObjectType())) {
2285+
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit);
2286+
return ExprError();
2287+
}
2288+
return Call;
2289+
}
2290+
2291+
22412292
static ExprResult SemaPointerAuthStringDiscriminator(Sema &S, CallExpr *call) {
22422293
if (checkPointerAuthEnabled(S, call)) return ExprError();
22432294

@@ -2256,6 +2307,39 @@ static ExprResult SemaPointerAuthStringDiscriminator(Sema &S, CallExpr *call) {
22562307
return call;
22572308
}
22582309

2310+
static ExprResult SemaGetVTablePointer(Sema &S, CallExpr *call) {
2311+
if (checkArgCount(S, call, 1))
2312+
return ExprError();
2313+
auto rvalue = S.DefaultFunctionArrayLvalueConversion(call->getArg(0));
2314+
if (rvalue.isInvalid())
2315+
return ExprError();
2316+
call->setArg(0, rvalue.get());
2317+
auto expression = call->getArg(0);
2318+
QualType expressionType = expression->getType();
2319+
const CXXRecordDecl *objectType = expressionType->getPointeeCXXRecordDecl();
2320+
if (!expressionType->isPointerType() || !objectType) {
2321+
S.Diag(expression->getBeginLoc(),
2322+
diag::err_get_vtable_pointer_incorrect_type)
2323+
<< 0 << expressionType;
2324+
return ExprError();
2325+
}
2326+
if (S.RequireCompleteType(
2327+
expression->getBeginLoc(), expressionType->getPointeeType(),
2328+
diag::err_get_vtable_pointer_requires_complete_type)) {
2329+
return ExprError();
2330+
}
2331+
2332+
if (!objectType->isPolymorphic()) {
2333+
S.Diag(expression->getBeginLoc(),
2334+
diag::err_get_vtable_pointer_incorrect_type)
2335+
<< 1 << objectType;
2336+
return ExprError();
2337+
}
2338+
QualType returnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
2339+
call->setType(returnType);
2340+
return call;
2341+
}
2342+
22592343
static ExprResult SemaBuiltinLaunder(Sema &S, CallExpr *TheCall) {
22602344
if (checkArgCount(S, TheCall, 1))
22612345
return ExprError();
@@ -2893,12 +2977,16 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
28932977
case Builtin::BI__builtin_ptrauth_auth:
28942978
return SemaPointerAuthSignOrAuth(*this, TheCall, PAO_Auth,
28952979
/*constant*/ false);
2980+
case Builtin::BI__builtin_get_vtable_pointer:
2981+
return SemaGetVTablePointer(*this, TheCall);
28962982
case Builtin::BI__builtin_ptrauth_sign_generic_data:
28972983
return SemaPointerAuthSignGenericData(*this, TheCall);
28982984
case Builtin::BI__builtin_ptrauth_auth_and_resign:
28992985
return SemaPointerAuthAuthAndResign(*this, TheCall);
29002986
case Builtin::BI__builtin_ptrauth_string_discriminator:
29012987
return SemaPointerAuthStringDiscriminator(*this, TheCall);
2988+
case Builtin::BI__builtin_virtual_member_address:
2989+
return SemaVirtualMemberAddress(*this, TheCall);
29022990
// OpenCL v2.0, s6.13.16 - Pipe functions
29032991
case Builtin::BIread_pipe:
29042992
case Builtin::BIwrite_pipe:

0 commit comments

Comments
 (0)