Skip to content

Commit 93314bd

Browse files
authored
[clang][PAC] Add __builtin_get_vtable_pointer (#139790)
With pointer authentication it becomes non-trivial to correctly load the vtable pointer of a polymorphic object. __builtin_get_vtable_pointer is a function that performs the load and performs the appropriate authentication operations if necessary.
1 parent e8b0a16 commit 93314bd

File tree

8 files changed

+595
-0
lines changed

8 files changed

+595
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,6 +3074,41 @@ following way:
30743074
30753075
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
30763076
3077+
``__builtin_get_vtable_pointer``
3078+
--------------------------------
3079+
3080+
``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
3081+
pointer from an instance of a polymorphic C++ class. This builtin is needed
3082+
for directly loading the vtable pointer when on platforms using
3083+
:doc:`PointerAuthentication`.
3084+
3085+
**Syntax**:
3086+
3087+
.. code-block:: c++
3088+
3089+
__builtin_get_vtable_pointer(PolymorphicClass*)
3090+
3091+
**Example of Use**:
3092+
3093+
.. code-block:: c++
3094+
3095+
struct PolymorphicClass {
3096+
virtual ~PolymorphicClass();
3097+
};
3098+
3099+
PolymorphicClass anInstance;
3100+
const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
3101+
3102+
**Description**:
3103+
3104+
The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
3105+
pointer from a polymorphic C++ type. If the target platform authenticates
3106+
vtable pointers, this builtin will perform the authentication and produce
3107+
the underlying raw pointer. The object being queried must be polymorphic,
3108+
and so must also be a complete type.
3109+
3110+
Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
3111+
30773112
``__builtin_call_with_static_chain``
30783113
------------------------------------
30793114

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ Non-comprehensive list of changes in this release
317317
``sizeof`` or ``typeof`` expression. (#GH138444)
318318
- Deprecation warning is emitted for the deprecated ``__reference_binds_to_temporary`` intrinsic.
319319
``__reference_constructs_from_temporary`` should be used instead. (#GH44056)
320+
- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
321+
polymorphic object.
320322

321323
New Compiler Flags
322324
------------------

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,12 @@ 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+
973979
// GCC exception builtins
974980
def EHReturn : Builtin {
975981
let Spellings = ["__builtin_eh_return"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12803,6 +12803,14 @@ def err_bit_cast_non_trivially_copyable : Error<
1280312803
def err_bit_cast_type_size_mismatch : Error<
1280412804
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
1280512805

12806+
def err_get_vtable_pointer_incorrect_type
12807+
: Error<"__builtin_get_vtable_pointer requires an argument of%select{| "
12808+
"polymorphic}0 class pointer type"
12809+
", but %1 %select{was provided|has no virtual methods}0">;
12810+
def err_get_vtable_pointer_requires_complete_type
12811+
: Error<"__builtin_get_vtable_pointer requires an argument with a complete "
12812+
"type, but %0 is incomplete">;
12813+
1280612814
// SYCL-specific diagnostics
1280712815
def warn_sycl_kernel_num_of_template_params : Warning<
1280812816
"'sycl_kernel' attribute only applies to a function template with at least"

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "CGDebugInfo.h"
1818
#include "CGObjCRuntime.h"
1919
#include "CGOpenCLRuntime.h"
20+
#include "CGPointerAuthInfo.h"
2021
#include "CGRecordLayout.h"
2122
#include "CGValue.h"
2223
#include "CodeGenFunction.h"
@@ -5621,6 +5622,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
56215622
return RValue::get(Result);
56225623
}
56235624

5625+
case Builtin::BI__builtin_get_vtable_pointer: {
5626+
const Expr *Target = E->getArg(0);
5627+
QualType TargetType = Target->getType();
5628+
const CXXRecordDecl *Decl = TargetType->getPointeeCXXRecordDecl();
5629+
assert(Decl);
5630+
auto ThisAddress = EmitPointerWithAlignment(Target);
5631+
assert(ThisAddress.isValid());
5632+
llvm::Value *VTablePointer =
5633+
GetVTablePtr(ThisAddress, Int8PtrTy, Decl, VTableAuthMode::MustTrap);
5634+
return RValue::get(VTablePointer);
5635+
}
5636+
56245637
case Builtin::BI__exception_code:
56255638
case Builtin::BI_exception_code:
56265639
return RValue::get(EmitSEHExceptionCode());

clang/lib/Sema/SemaChecking.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,40 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
18291829
return Call;
18301830
}
18311831

1832+
static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
1833+
if (S.checkArgCount(Call, 1))
1834+
return ExprError();
1835+
Expr *FirstArg = Call->getArg(0);
1836+
ExprResult FirstValue = S.DefaultFunctionArrayLvalueConversion(FirstArg);
1837+
if (FirstValue.isInvalid())
1838+
return ExprError();
1839+
Call->setArg(0, FirstValue.get());
1840+
QualType FirstArgType = FirstArg->getType();
1841+
if (FirstArgType->canDecayToPointerType() && FirstArgType->isArrayType())
1842+
FirstArgType = S.Context.getDecayedType(FirstArgType);
1843+
1844+
const CXXRecordDecl *FirstArgRecord = FirstArgType->getPointeeCXXRecordDecl();
1845+
if (!FirstArgRecord) {
1846+
S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
1847+
<< /*isPolymorphic=*/0 << FirstArgType;
1848+
return ExprError();
1849+
}
1850+
if (S.RequireCompleteType(
1851+
FirstArg->getBeginLoc(), FirstArgType->getPointeeType(),
1852+
diag::err_get_vtable_pointer_requires_complete_type)) {
1853+
return ExprError();
1854+
}
1855+
1856+
if (!FirstArgRecord->isPolymorphic()) {
1857+
S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
1858+
<< /*isPolymorphic=*/1 << FirstArgRecord;
1859+
return ExprError();
1860+
}
1861+
QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
1862+
Call->setType(ReturnType);
1863+
return Call;
1864+
}
1865+
18321866
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
18331867
if (S.checkArgCount(TheCall, 1))
18341868
return ExprError();
@@ -2727,6 +2761,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
27272761
return PointerAuthAuthAndResign(*this, TheCall);
27282762
case Builtin::BI__builtin_ptrauth_string_discriminator:
27292763
return PointerAuthStringDiscriminator(*this, TheCall);
2764+
2765+
case Builtin::BI__builtin_get_vtable_pointer:
2766+
return GetVTablePointer(*this, TheCall);
2767+
27302768
// OpenCL v2.0, s6.13.16 - Pipe functions
27312769
case Builtin::BIread_pipe:
27322770
case Builtin::BIwrite_pipe:

0 commit comments

Comments
 (0)