Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
33 changes: 33 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3062,6 +3062,39 @@ 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.

**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 @@ -291,6 +291,8 @@ Non-comprehensive list of changes in this release
different than before.
- Fixed a crash when a VLA with an invalid size expression was used within a
``sizeof`` or ``typeof`` expression. (#GH138444)
- 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 @@ -12729,6 +12729,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 @@ -5365,6 +5366,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
35 changes: 35 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,37 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) {
return Call;
}

static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) {
if (S.checkArgCount(Call, 1))
return ExprError();
ExprResult ThisArg = S.DefaultFunctionArrayLvalueConversion(Call->getArg(0));
if (ThisArg.isInvalid())
return ExprError();
Call->setArg(0, ThisArg.get());
const Expr *Subject = Call->getArg(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const Expr *Subject = Call->getArg(0);
const Expr *Subject = ThisArg.get();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FirstArg would be a better name, or jusst Arg

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

goddammit forgot this change

QualType SubjectType = Subject->getType();
const CXXRecordDecl *SubjectRecord = SubjectType->getPointeeCXXRecordDecl();
if (!SubjectType->isPointerType() || !SubjectRecord) {
S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
<< 0 << SubjectType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<< 0 << SubjectType;
<< /*Class* /0 << SubjectType;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cor3ntin for both of these (/Class/ and /Polymorpic/) should that be using =*/? and also I wonder if we can make clang format aware of the /*ParameterName=*/ idiom for parameters of bool and (maybe) integer types? (recognizing that in the context of operator << in diags this is a different problem)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use = there (or not consistently). The important bit is to know what's happening.
You could look into %enum_select as a better solution

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we could make every diag encode the type of the diag as a function type, and have things fail at compile time when possible. This would be super great as it would provide a great stress test for clangs template code :D :D :D

return ExprError();
}
if (S.RequireCompleteType(
Subject->getBeginLoc(), SubjectType->getPointeeType(),
diag::err_get_vtable_pointer_requires_complete_type)) {
return ExprError();
}

if (!SubjectRecord->isPolymorphic()) {
S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type)
<< 1 << SubjectRecord;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<< 1 << SubjectRecord;
<< /* Polymorphic*/1 << SubjectRecord;

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 @@ -2719,6 +2750,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