Skip to content

Commit 871714b

Browse files
committed
[clang][PAC] Add __builtin_get_vtable_pointer
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 952b680 commit 871714b

File tree

8 files changed

+526
-0
lines changed

8 files changed

+526
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,39 @@ following way:
30623062
30633063
Query for this feature with ``__has_builtin(__builtin_offsetof)``.
30643064
3065+
``__builtin_get_vtable_pointer``
3066+
--------------------------------
3067+
3068+
``__builtin_get_vtable_pointer`` loads and authenticates the primary vtable
3069+
pointer from an instance of a polymorphic C++ class.
3070+
3071+
**Syntax**:
3072+
3073+
.. code-block:: c++
3074+
3075+
__builtin_get_vtable_pointer(PolymorphicClass*)
3076+
3077+
**Example of Use**:
3078+
3079+
.. code-block:: c++
3080+
3081+
struct PolymorphicClass {
3082+
virtual ~PolymorphicClass();
3083+
};
3084+
3085+
PolymorphicClass anInstance;
3086+
const void* vtablePointer = __builtin_get_vtable_pointer(&anInstance);
3087+
3088+
**Description**:
3089+
3090+
The ``__builtin_get_vtable_pointer`` builtin loads the primary vtable
3091+
pointer from a polymorphic C++ type. If the target platform authenticates
3092+
vtable pointers, this builtin will perform the authentication and produce
3093+
the underlying raw pointer. The object being queried must be polymorphic,
3094+
and so must also be a complete type.
3095+
3096+
Query for this feature with ``__has_builtin(__builtin_get_vtable_pointer)``.
3097+
30653098
``__builtin_call_with_static_chain``
30663099
------------------------------------
30673100

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ Non-comprehensive list of changes in this release
291291
different than before.
292292
- Fixed a crash when a VLA with an invalid size expression was used within a
293293
``sizeof`` or ``typeof`` expression. (#GH138444)
294+
- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
295+
polymorphic object.
294296

295297
New Compiler Flags
296298
------------------

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
@@ -12729,6 +12729,14 @@ def err_bit_cast_non_trivially_copyable : Error<
1272912729
def err_bit_cast_type_size_mismatch : Error<
1273012730
"size of '__builtin_bit_cast' source type %0 does not match destination type %1 (%2 vs %3 bytes)">;
1273112731

12732+
def err_get_vtable_pointer_incorrect_type
12733+
: Error<"__builtin_get_vtable_pointer requires an argument of%select{| "
12734+
"polymorphic}0 class pointer type"
12735+
", but %1 %select{was provided|has no virtual methods}0">;
12736+
def err_get_vtable_pointer_requires_complete_type
12737+
: Error<"__builtin_get_vtable_pointer requires an argument with a complete "
12738+
"type, but %0 is incomplete">;
12739+
1273212740
// SYCL-specific diagnostics
1273312741
def warn_sycl_kernel_num_of_template_params : Warning<
1273412742
"'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"
@@ -5365,6 +5366,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
53655366
return RValue::get(Result);
53665367
}
53675368

5369+
case Builtin::BI__builtin_get_vtable_pointer: {
5370+
const Expr *Target = E->getArg(0);
5371+
QualType TargetType = Target->getType();
5372+
const CXXRecordDecl *Decl = TargetType->getPointeeCXXRecordDecl();
5373+
assert(Decl);
5374+
auto ThisAddress = EmitPointerWithAlignment(Target);
5375+
assert(ThisAddress.isValid());
5376+
llvm::Value *VTablePointer =
5377+
GetVTablePtr(ThisAddress, Int8PtrTy, Decl, VTableAuthMode::MustTrap);
5378+
return RValue::get(VTablePointer);
5379+
}
5380+
53685381
case Builtin::BI__exception_code:
53695382
case Builtin::BI_exception_code:
53705383
return RValue::get(EmitSEHExceptionCode());

clang/lib/Sema/SemaChecking.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,39 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
18251825
return Call;
18261826
}
18271827

1828+
static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
1829+
if (S.checkArgCount(Call, 1))
1830+
return ExprError();
1831+
ExprResult ThisArg = S.DefaultFunctionArrayLvalueConversion(Call->getArg(0));
1832+
if (ThisArg.isInvalid())
1833+
return ExprError();
1834+
Call->setArg(0, ThisArg.get());
1835+
const Expr *Subject = Call->getArg(0);
1836+
QualType SubjectType = Subject->getType();
1837+
const CXXRecordDecl *SubjectRecord = SubjectType->getPointeeCXXRecordDecl();
1838+
if (!SubjectType->isPointerType() || !SubjectRecord) {
1839+
S.Diag(Subject->getBeginLoc(),
1840+
diag::err_get_vtable_pointer_incorrect_type)
1841+
<< 0 << SubjectType;
1842+
return ExprError();
1843+
}
1844+
if (S.RequireCompleteType(
1845+
Subject->getBeginLoc(), SubjectType->getPointeeType(),
1846+
diag::err_get_vtable_pointer_requires_complete_type)) {
1847+
return ExprError();
1848+
}
1849+
1850+
if (!SubjectRecord->isPolymorphic()) {
1851+
S.Diag(Subject->getBeginLoc(),
1852+
diag::err_get_vtable_pointer_incorrect_type)
1853+
<< 1 << SubjectRecord;
1854+
return ExprError();
1855+
}
1856+
QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst());
1857+
Call->setType(ReturnType);
1858+
return Call;
1859+
}
1860+
18281861
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
18291862
if (S.checkArgCount(TheCall, 1))
18301863
return ExprError();
@@ -2719,6 +2752,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
27192752
return PointerAuthAuthAndResign(*this, TheCall);
27202753
case Builtin::BI__builtin_ptrauth_string_discriminator:
27212754
return PointerAuthStringDiscriminator(*this, TheCall);
2755+
2756+
case Builtin::BI__builtin_get_vtable_pointer:
2757+
return GetVTablePointer(*this, TheCall);
2758+
27222759
// OpenCL v2.0, s6.13.16 - Pipe functions
27232760
case Builtin::BIread_pipe:
27242761
case Builtin::BIwrite_pipe:

0 commit comments

Comments
 (0)