Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3074,6 +3074,41 @@ 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. This builtin is needed
for directly loading the vtable pointer when on platforms using
:doc:`PointerAuthentication`.

**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``
------------------------------------

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ Non-comprehensive list of changes in this release
``sizeof`` or ``typeof`` expression. (#GH138444)
- Deprecation warning is emitted for the deprecated ``__reference_binds_to_temporary`` intrinsic.
``__reference_constructs_from_temporary`` should be used instead. (#GH44056)
- Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a
polymorphic object.

New Compiler Flags
------------------
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,12 @@ 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*)";
}

// GCC exception builtins
def EHReturn : Builtin {
let Spellings = ["__builtin_eh_return"];
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12803,6 +12803,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"
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -5621,6 +5622,18 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Result);
}

case Builtin::BI__builtin_get_vtable_pointer: {
const Expr *Target = E->getArg(0);
QualType TargetType = Target->getType();
const CXXRecordDecl *Decl = TargetType->getPointeeCXXRecordDecl();
assert(Decl);
auto ThisAddress = EmitPointerWithAlignment(Target);
assert(ThisAddress.isValid());
llvm::Value *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());
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1829,6 +1829,40 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
return Call;
}

static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
if (S.checkArgCount(Call, 1))
return ExprError();
Expr *FirstArg = Call->getArg(0);
ExprResult FirstValue = S.DefaultFunctionArrayLvalueConversion(FirstArg);
if (FirstValue.isInvalid())
return ExprError();
Call->setArg(0, FirstValue.get());
QualType FirstArgType = FirstArg->getType();
if (FirstArgType->canDecayToPointerType() && FirstArgType->isArrayType())
FirstArgType = S.Context.getDecayedType(FirstArgType);

const CXXRecordDecl *FirstArgRecord = FirstArgType->getPointeeCXXRecordDecl();
if (!FirstArgRecord) {
S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
<< /*isPolymorphic=*/0 << FirstArgType;
return ExprError();
}
if (S.RequireCompleteType(
FirstArg->getBeginLoc(), FirstArgType->getPointeeType(),
diag::err_get_vtable_pointer_requires_complete_type)) {
return ExprError();
}

if (!FirstArgRecord->isPolymorphic()) {
S.Diag(FirstArg->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
<< /*isPolymorphic=*/1 << FirstArgRecord;
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();
Expand Down Expand Up @@ -2727,6 +2761,10 @@ 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);

// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
Expand Down
Loading