Skip to content

Commit efbbd08

Browse files
ahmedbougachadtapuska
authored andcommitted
[clang] Add builtin_get_vtable_pointer and virtual_member_address
1 parent fffc12e commit efbbd08

File tree

9 files changed

+706
-0
lines changed

9 files changed

+706
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,6 +3031,39 @@ following way:
30313031
30323032
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
30333033
3034+
``__builtin_get_vtable_pointer``
3035+
--------------------------------
3036+
3037+
``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
3038+
pointer from an instance of a polymorphic C++ class.
3039+
3040+
**Syntax**:
3041+
3042+
.. code-block:: c++
3043+
3044+
__builtin_get_vtable_pointer(PolymorphicClass*)
3045+
3046+
**Example of Use**:
3047+
3048+
.. code-block:: c++
3049+
3050+
struct PolymorphicClass {
3051+
virtual ~PolymorphicClass();
3052+
};
3053+
3054+
PolymorphicClass anInstance;
3055+
const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
3056+
3057+
**Description**:
3058+
3059+
The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
3060+
pointer from a polymorphic C++ type. If the target platform authenticates
3061+
vtable pointers, this builtin will perform the authentication and produce
3062+
the underlying raw pointer. The object being queried must be polymorphic,
3063+
and so must also be a complete type.
3064+
3065+
Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
3066+
30343067
``__builtin_call_with_static_chain``
30353068
------------------------------------
30363069

clang/include/clang/Basic/Builtins.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,18 @@ def IsWithinLifetime : LangBuiltin<"CXX_LANG"> {
970970
let Prototype = "bool(void*)";
971971
}
972972

973+
def GetVtablePointer : LangBuiltin<"CXX_LANG"> {
974+
let Spellings = ["__builtin_get_vtable_pointer"];
975+
let Attributes = [CustomTypeChecking, NoThrow, Const];
976+
let Prototype = "void*(void*)";
977+
}
978+
979+
def VirtualMemberAddress : Builtin {
980+
let Spellings = ["__builtin_virtual_member_address"];
981+
let Attributes = [CustomTypeChecking, NoThrow, Const];
982+
let Prototype = "void*(void*,void*)";
983+
}
984+
973985
// GCC exception builtins
974986
def EHReturn : Builtin {
975987
let Spellings = ["__builtin_eh_return"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,13 @@ def err_ptrauth_invalid_struct_attr_dynamic_class : Error<
10681068
"attribute ptrauth_struct cannot be used on class %0 or its subclasses "
10691069
"because it is a dynamic class">;
10701070

1071+
def err_virtual_member_lhs_cxxrec : Error<
1072+
"first argument to __builtin_virtual_member_address must have C++ class type">;
1073+
def err_virtual_member_addrof : Error<
1074+
"second argument to __builtin_virtual_member_address must be the address of a virtual C++ member function: for example '&Foo::func'">;
1075+
def err_virtual_member_inherit : Error<
1076+
"first argument to __builtin_virtual_member_address must have a type deriving from class where second argument was defined">;
1077+
10711078
/// main()
10721079
// static main() is not an error in C, just in C++.
10731080
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -12594,6 +12601,14 @@ def err_bit_cast_non_trivially_copyable : Error<
1259412601
def err_bit_cast_type_size_mismatch : Error<
1259512602
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
1259612603

12604+
12605+
def err_get_vtable_pointer_incorrect_type : Error<
12606+
"__builtin_get_vtable_pointer requires an argument of%select{| polymorphic}0 class pointer type"
12607+
", but %1 %select{was provided|has no virtual methods}0"
12608+
>;
12609+
def err_get_vtable_pointer_requires_complete_type : Error<
12610+
"__builtin_get_vtable_pointer requires an argument with a complete type, but %0 is incomplete">;
12611+
1259712612
// SYCL-specific diagnostics
1259812613
def warn_sycl_kernel_num_of_template_params : Warning<
1259912614
"'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
@@ -5347,6 +5347,38 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
53475347
return RValue::get(Result);
53485348
}
53495349

5350+
case Builtin::BI__builtin_virtual_member_address: {
5351+
Address This = EmitLValue(E->getArg(0)).getAddress();
5352+
APValue ConstMemFun;
5353+
E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr);
5354+
auto CXXMethod = cast<CXXMethodDecl>(ConstMemFun.getMemberPointerDecl());
5355+
const CGFunctionInfo &FInfo =
5356+
CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod);
5357+
auto Ty = CGM.getTypes().GetFunctionType(FInfo);
5358+
CGCallee VCallee = CGCallee::forVirtual(nullptr, CXXMethod, This, Ty);
5359+
auto Callee = VCallee.prepareConcreteCallee(*this).getFunctionPointer();
5360+
if (CGM.getCodeGenOpts().PointerAuth.FunctionPointers) {
5361+
auto Strip = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_strip);
5362+
Callee =
5363+
EmitRuntimeCall(Strip, {Builder.CreatePtrToInt(Callee, IntPtrTy),
5364+
llvm::ConstantInt::get(IntTy, 0)});
5365+
}
5366+
Callee = Builder.CreateBitOrPointerCast(Callee, Int8PtrTy);
5367+
return RValue::get(Callee);
5368+
}
5369+
5370+
case Builtin::BI__builtin_get_vtable_pointer: {
5371+
auto target = E->getArg(0);
5372+
auto type = target->getType();
5373+
auto decl = type->getPointeeCXXRecordDecl();
5374+
assert(decl);
5375+
auto thisAddress = EmitPointerWithAlignment(target);
5376+
assert(thisAddress.isValid());
5377+
auto vtablePointer =
5378+
GetVTablePtr(thisAddress, Int8PtrTy, decl, VTableAuthMode::MustTrap);
5379+
return RValue::get(vtablePointer);
5380+
}
5381+
53505382
case Builtin::BI__exception_code:
53515383
case Builtin::BI_exception_code:
53525384
return RValue::get(EmitSEHExceptionCode());

clang/lib/Sema/SemaChecking.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,57 @@ static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
18151815
return Call;
18161816
}
18171817

1818+
static ExprResult VirtualMemberAddress(Sema &S, CallExpr *Call) {
1819+
if (S.checkArgCount(Call, 2)) return ExprError();
1820+
1821+
for (int i = 0; i < 2; ++i) {
1822+
auto ArgRValue = S.DefaultFunctionArrayLvalueConversion(Call->getArg(1));
1823+
if (ArgRValue.isInvalid())
1824+
return ExprError();
1825+
1826+
auto Arg = ArgRValue.get();
1827+
Call->setArg(1, Arg);
1828+
}
1829+
1830+
if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent())
1831+
return Call;
1832+
1833+
auto ThisArg = Call->getArg(0);
1834+
auto ThisTy = ThisArg->getType();
1835+
if (!ThisTy->getAsCXXRecordDecl()) {
1836+
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec);
1837+
return ExprError();
1838+
}
1839+
1840+
auto MemFunArg = Call->getArg(1);
1841+
APValue Result;
1842+
if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) {
1843+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
1844+
return ExprError();
1845+
}
1846+
1847+
if (!Result.isMemberPointer() ||
1848+
!isa<CXXMethodDecl>(Result.getMemberPointerDecl())) {
1849+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
1850+
return ExprError();
1851+
}
1852+
1853+
auto CXXMethod = cast<CXXMethodDecl>(Result.getMemberPointerDecl());
1854+
if (!CXXMethod->isVirtual()) {
1855+
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof);
1856+
return ExprError();
1857+
}
1858+
1859+
if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() &&
1860+
!S.IsDerivedFrom(Call->getBeginLoc(), ThisTy,
1861+
CXXMethod->getFunctionObjectParameterType())) {
1862+
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit);
1863+
return ExprError();
1864+
}
1865+
return Call;
1866+
}
1867+
1868+
18181869
static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
18191870
if (checkPointerAuthEnabled(S, Call))
18201871
return ExprError();
@@ -1833,6 +1884,39 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
18331884
return Call;
18341885
}
18351886

1887+
static ExprResult GetVTablePointer(Sema &S, CallExpr *call) {
1888+
if (S.checkArgCount(call, 1))
1889+
return ExprError();
1890+
auto rvalue = S.DefaultFunctionArrayLvalueConversion(call->getArg(0));
1891+
if (rvalue.isInvalid())
1892+
return ExprError();
1893+
call->setArg(0, rvalue.get());
1894+
auto expression = call->getArg(0);
1895+
QualType expressionType = expression->getType();
1896+
const CXXRecordDecl *objectType = expressionType->getPointeeCXXRecordDecl();
1897+
if (!expressionType->isPointerType() || !objectType) {
1898+
S.Diag(expression->getBeginLoc(),
1899+
diag::err_get_vtable_pointer_incorrect_type)
1900+
<< 0 << expressionType;
1901+
return ExprError();
1902+
}
1903+
if (S.RequireCompleteType(
1904+
expression->getBeginLoc(), expressionType->getPointeeType(),
1905+
diag::err_get_vtable_pointer_requires_complete_type)) {
1906+
return ExprError();
1907+
}
1908+
1909+
if (!objectType->isPolymorphic()) {
1910+
S.Diag(expression->getBeginLoc(),
1911+
diag::err_get_vtable_pointer_incorrect_type)
1912+
<< 1 << objectType;
1913+
return ExprError();
1914+
}
1915+
QualType returnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
1916+
call->setType(returnType);
1917+
return call;
1918+
}
1919+
18361920
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
18371921
if (S.checkArgCount(TheCall, 1))
18381922
return ExprError();
@@ -2674,6 +2758,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
26742758
return PointerAuthAuthAndResign(*this, TheCall);
26752759
case Builtin::BI__builtin_ptrauth_string_discriminator:
26762760
return PointerAuthStringDiscriminator(*this, TheCall);
2761+
2762+
case Builtin::BI__builtin_get_vtable_pointer:
2763+
return GetVTablePointer(*this, TheCall);
2764+
case Builtin::BI__builtin_virtual_member_address:
2765+
return VirtualMemberAddress(*this, TheCall);
2766+
26772767
// OpenCL v2.0, s6.13.16 - Pipe functions
26782768
case Builtin::BIread_pipe:
26792769
case Builtin::BIwrite_pipe:

0 commit comments

Comments
 (0)