[clang][ObjC][PAC] Add ptrauth protections to objective-c (#147899)

This PR introduces the use of pointer authentication to objective-c[++].

This includes:

* __ptrauth qualifier support for ivars
* protection of isa and super fields
* protection of SEL typed ivars
* protection of class_ro_t data
* protection of methodlist pointers and content
This commit is contained in:
Oliver Hunt 2025-07-14 19:32:18 -07:00 committed by GitHub
parent 90ef114a33
commit 451a9ce9ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 983 additions and 42 deletions

View File

@ -1022,7 +1022,9 @@ Arm and AArch64 Support
`as specified here <https://github.com/ARM-software/acle/blob/main/main/acle.md#modal-8-bit-floating-point-extensions>`_
is now available.
- Support has been added for the following processors (command-line identifiers in parentheses):
- Arm Cortex-A320 (``cortex-a320``)
- For ARM targets, cc1as now considers the FPU's features for the selected CPU or Architecture.
- The ``+nosimd`` attribute is now fully supported for ARM. Previously, this had no effect when being used with
ARM targets, however this will now disable NEON instructions being generated. The ``simd`` option is
@ -1030,7 +1032,19 @@ Arm and AArch64 Support
- When a feature that depends on NEON (``simd``) is used, NEON is now automatically enabled.
- When NEON is disabled (``+nosimd``), all features that depend on NEON will now be disabled.
- Support for __ptrauth type qualifier has been added.
- Pointer authentication
- Support for __ptrauth type qualifier has been added.
- Objective-C adoption of pointer authentication
- ``isa`` and ``super`` pointers are protected with address diversity and separate
usage specific discriminators.
- methodlist pointers and content are protected with address diversity and methodlist
pointers have a usage specific discriminator.
- ``class_ro_t`` pointers are protected with address diversity and usage specific
discriminators.
- ``SEL`` typed ivars are protected with address diversity and usage specific
discriminators.
- For AArch64, added support for generating executable-only code sections by using the
``-mexecute-only`` or ``-mpure-code`` compiler flags. (#GH125688)

View File

@ -2300,6 +2300,8 @@ public:
return getTypeDeclType(getObjCSelDecl());
}
PointerAuthQualifier getObjCMemberSelTypePtrAuth();
/// Retrieve the typedef declaration corresponding to the predefined
/// Objective-C 'Class' type.
TypedefDecl *getObjCClassDecl() const;

View File

@ -160,6 +160,12 @@ FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos)
FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini)
FEATURE(ptrauth_init_fini_address_discrimination, LangOpts.PointerAuthInitFiniAddressDiscrimination)
FEATURE(ptrauth_elf_got, LangOpts.PointerAuthELFGOT)
FEATURE(ptrauth_objc_isa, LangOpts.PointerAuthObjcIsa)
FEATURE(ptrauth_objc_interface_sel, LangOpts.PointerAuthObjcInterfaceSel)
FEATURE(ptrauth_objc_signable_class, true)
FEATURE(ptrauth_objc_method_list_pointer, LangOpts.PointerAuthCalls)
EXTENSION(swiftcc,
PP.getTargetInfo().checkCallingConvention(CC_Swift) ==
clang::TargetInfo::CCCR_OK)

View File

@ -133,6 +133,11 @@ LANGOPT(PointerAuthInitFiniAddressDiscrimination, 1, 0, NotCompatible,
LANGOPT(PointerAuthELFGOT, 1, 0, NotCompatible, "authenticate pointers from GOT")
LANGOPT(AArch64JumpTableHardening, 1, 0, NotCompatible, "use hardened lowering for jump-table dispatch")
LANGOPT(PointerAuthObjcIsa, 1, 0, NotCompatible, "authentication of isa and super pointers in ObjC instances")
LANGOPT(PointerAuthObjcInterfaceSel, 1, 0, NotCompatible, "authentication of SEL fields of ObjC interfaces")
LANGOPT(PointerAuthObjcInterfaceSelKey, 16, 0, NotCompatible, "authentication key for SEL fields of ObjC interfaces")
LANGOPT(PointerAuthObjcClassROPointers, 1, 0, Benign, "class_ro_t pointer authentication")
LANGOPT(DoubleSquareBracketAttributes, 1, 0, NotCompatible, "'[[]]' attributes extension for all language standard modes")
LANGOPT(ExperimentalLateParseAttributes, 1, 0, NotCompatible, "experimental late parsing of attributes")

View File

@ -27,6 +27,26 @@ namespace clang {
/// .fini_array. The value is ptrauth_string_discriminator("init_fini")
constexpr uint16_t InitFiniPointerConstantDiscriminator = 0xD9D4;
/// Constant discriminator to be used with method list pointers. The value is
/// ptrauth_string_discriminator("method_list_t")
constexpr uint16_t MethodListPointerConstantDiscriminator = 0xC310;
/// Constant discriminator to be used with objective-c isa pointers. The value
/// is ptrauth_string_discriminator("isa")
constexpr uint16_t IsaPointerConstantDiscriminator = 0x6AE1;
/// Constant discriminator to be used with objective-c superclass pointers.
/// The value is ptrauth_string_discriminator("objc_class:superclass")
constexpr uint16_t SuperPointerConstantDiscriminator = 0xB5AB;
/// Constant discriminator to be used with objective-c sel pointers. The value
/// is ptrauth_string_discriminator("sel")
constexpr uint16_t SelPointerConstantDiscriminator = 0x57c2;
/// Constant discriminator to be used with objective-c class_ro_t pointers.
/// The value is ptrauth_string_discriminator("class_data_bits")
constexpr uint16_t ClassROConstantDiscriminator = 0x61F8;
constexpr unsigned PointerAuthKeyNone = -1;
/// Constant discriminator for std::type_info vtable pointers: 0xB1EA/45546
@ -202,6 +222,21 @@ struct PointerAuthOptions {
/// The ABI for function addresses in .init_array and .fini_array
PointerAuthSchema InitFiniPointers;
/// The ABI for Objective-C method lists.
PointerAuthSchema ObjCMethodListFunctionPointers;
/// The ABI for a reference to an Objective-C method list in _class_ro_t.
PointerAuthSchema ObjCMethodListPointer;
/// The ABI for Objective-C isa pointers.
PointerAuthSchema ObjCIsaPointers;
/// The ABI for Objective-C superclass pointers.
PointerAuthSchema ObjCSuperPointers;
/// The ABI for Objective-C class_ro_t pointers.
PointerAuthSchema ObjCClassROPointers;
};
} // end namespace clang

View File

@ -4504,6 +4504,9 @@ defm ptrauth_init_fini_address_discrimination : OptInCC1FFlag<"ptrauth-init-fini
"Enable address discrimination of function pointers in init/fini arrays">;
defm ptrauth_elf_got : OptInCC1FFlag<"ptrauth-elf-got", "Enable authentication of pointers from GOT (ELF only)">;
defm aarch64_jump_table_hardening: OptInCC1FFlag<"aarch64-jump-table-hardening", "Use hardened lowering for jump-table dispatch">;
defm ptrauth_objc_isa : OptInCC1FFlag<"ptrauth-objc-isa", "Enable signing and authentication of Objective-C object's 'isa' field">;
defm ptrauth_objc_interface_sel : OptInCC1FFlag<"ptrauth-objc-interface-sel", "Enable signing and authentication of Objective-C object's 'SEL' fields">;
defm ptrauth_objc_class_ro : OptInCC1FFlag<"ptrauth-objc-class-ro", "Enable signing and authentication for ObjC class_ro pointers">;
}
def fenable_matrix : Flag<["-"], "fenable-matrix">, Group<f_Group>,

View File

@ -9783,6 +9783,17 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const {
return ObjCProtocolClassDecl;
}
PointerAuthQualifier ASTContext::getObjCMemberSelTypePtrAuth() {
if (!getLangOpts().PointerAuthObjcInterfaceSel)
return PointerAuthQualifier();
return PointerAuthQualifier::Create(
getLangOpts().PointerAuthObjcInterfaceSelKey,
/*isAddressDiscriminated=*/true, SelPointerConstantDiscriminator,
PointerAuthenticationMode::SignAndAuth,
/*isIsaPointer=*/false,
/*authenticatesNullValues=*/false);
}
//===----------------------------------------------------------------------===//
// __builtin_va_list Construction Functions
//===----------------------------------------------------------------------===//

View File

@ -853,9 +853,24 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) {
offset += size;
index++;
};
auto addSignedHeaderField =
[&](llvm::Value *Value, const PointerAuthSchema &Schema,
GlobalDecl Decl, QualType Type, CharUnits Size, const Twine &Name) {
auto StorageAddress = projectField(index, Name);
if (Schema) {
auto AuthInfo = EmitPointerAuthInfo(
Schema, StorageAddress.emitRawPointer(*this), Decl, Type);
Value = EmitPointerAuthSign(AuthInfo, Value);
}
Builder.CreateStore(Value, StorageAddress);
offset += Size;
index++;
};
if (!IsOpenCL) {
addHeaderField(isa, getPointerSize(), "block.isa");
addSignedHeaderField(
isa, CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers, GlobalDecl(),
QualType(), getPointerSize(), "block.isa");
addHeaderField(llvm::ConstantInt::get(IntTy, flags.getBitMask()),
getIntSize(), "block.flags");
addHeaderField(llvm::ConstantInt::get(IntTy, 0), getIntSize(),
@ -1285,7 +1300,9 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM,
if (IsWindows)
fields.addNullPointer(CGM.Int8PtrPtrTy);
else
fields.add(CGM.getNSConcreteGlobalBlock());
fields.addSignedPointer(CGM.getNSConcreteGlobalBlock(),
CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers,
GlobalDecl(), QualType());
// __flags
BlockFlags flags = BLOCK_IS_GLOBAL;

View File

@ -1193,16 +1193,23 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
ivarAddr = ivarAddr.withElementType(bitcastType);
llvm::LoadInst *load = Builder.CreateLoad(ivarAddr, "load");
load->setAtomic(llvm::AtomicOrdering::Unordered);
llvm::Value *ivarVal = load;
if (PointerAuthQualifier PAQ = ivar->getType().getPointerAuth()) {
CGPointerAuthInfo SrcInfo = EmitPointerAuthInfo(PAQ, ivarAddr);
CGPointerAuthInfo TargetInfo =
CGM.getPointerAuthInfoForType(getterMethod->getReturnType());
ivarVal = emitPointerAuthResign(ivarVal, ivar->getType(), SrcInfo,
TargetInfo, /*isKnownNonNull=*/false);
}
// Store that value into the return address. Doing this with a
// bitcast is likely to produce some pretty ugly IR, but it's not
// the *most* terrible thing in the world.
llvm::Type *retTy = ConvertType(getterMethod->getReturnType());
uint64_t retTySize = CGM.getDataLayout().getTypeSizeInBits(retTy);
llvm::Value *ivarVal = load;
if (ivarSize > retTySize) {
bitcastType = llvm::Type::getIntNTy(getLLVMContext(), retTySize);
ivarVal = Builder.CreateTrunc(load, bitcastType);
ivarVal = Builder.CreateTrunc(ivarVal, bitcastType);
}
Builder.CreateStore(ivarVal, ReturnValue.withElementType(bitcastType));
@ -1214,6 +1221,16 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
case PropertyImplStrategy::GetSetProperty: {
llvm::FunctionCallee getPropertyFn =
CGM.getObjCRuntime().GetPropertyGetFunction();
if (ivar->getType().getPointerAuth()) {
// This currently cannot be hit, but if we ever allow objc pointers
// to be signed, this will become possible. Reaching here would require
// a copy, weak, etc property backed by an authenticated pointer.
CGM.ErrorUnsupported(propImpl,
"Obj-C getter requiring pointer authentication");
return;
}
if (!getPropertyFn) {
CGM.ErrorUnsupported(propImpl, "Obj-C getter requiring atomic copy");
return;
@ -1269,7 +1286,9 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar, 0);
QualType ivarType = ivar->getType();
switch (getEvaluationKind(ivarType)) {
auto EvaluationKind = getEvaluationKind(ivarType);
assert(!ivarType.getPointerAuth() || EvaluationKind == TEK_Scalar);
switch (EvaluationKind) {
case TEK_Complex: {
ComplexPairTy pair = EmitLoadOfComplex(LV, SourceLocation());
EmitStoreOfComplex(pair, MakeAddrLValue(ReturnValue, ivarType),
@ -1287,6 +1306,11 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
case TEK_Scalar: {
llvm::Value *value;
if (propType->isReferenceType()) {
if (ivarType.getPointerAuth()) {
CGM.ErrorUnsupported(propImpl,
"Obj-C getter for authenticated reference type");
return;
}
value = LV.getAddress().emitRawPointer(*this);
} else {
// We want to load and autoreleaseReturnValue ARC __weak ivars.
@ -1300,7 +1324,19 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl,
// Otherwise we want to do a simple load, suppressing the
// final autorelease.
} else {
value = EmitLoadOfLValue(LV, SourceLocation()).getScalarVal();
if (PointerAuthQualifier PAQ = ivar->getType().getPointerAuth()) {
Address ivarAddr = LV.getAddress();
llvm::LoadInst *LoadInst = Builder.CreateLoad(ivarAddr, "load");
llvm::Value *Load = LoadInst;
auto SrcInfo = EmitPointerAuthInfo(PAQ, ivarAddr);
auto TargetInfo =
CGM.getPointerAuthInfoForType(getterMethod->getReturnType());
Load = emitPointerAuthResign(Load, ivarType, SrcInfo, TargetInfo,
/*isKnownNonNull=*/false);
value = Load;
} else
value = EmitLoadOfLValue(LV, SourceLocation()).getScalarVal();
AutoreleaseResult = false;
}
@ -1490,6 +1526,14 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl,
llvm::Value *load = Builder.CreateLoad(argAddr);
if (PointerAuthQualifier PAQ = ivar->getType().getPointerAuth()) {
QualType PropertyType = propImpl->getPropertyDecl()->getType();
CGPointerAuthInfo SrcInfo = CGM.getPointerAuthInfoForType(PropertyType);
CGPointerAuthInfo TargetInfo = EmitPointerAuthInfo(PAQ, ivarAddr);
load = emitPointerAuthResign(load, ivar->getType(), SrcInfo, TargetInfo,
/*isKnownNonNull=*/false);
}
// Perform an atomic store. There are no memory ordering requirements.
llvm::StoreInst *store = Builder.CreateStore(load, ivarAddr);
store->setAtomic(llvm::AtomicOrdering::Unordered);

View File

@ -1935,7 +1935,9 @@ CGObjCCommonMac::GenerateConstantNSString(const StringLiteral *Literal) {
auto Fields = Builder.beginStruct(NSConstantStringType);
// Class pointer.
Fields.add(Class);
Fields.addSignedPointer(Class,
CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers,
GlobalDecl(), QualType());
// String pointer.
llvm::Constant *C =
@ -4975,10 +4977,7 @@ enum ImageInfoFlags {
eImageInfo_GCOnly = (1 << 2),
eImageInfo_OptimizedByDyld = (1 << 3), // This flag is set by the dyld shared cache.
// A flag indicating that the module has no instances of a @synthesize of a
// superclass variable. This flag used to be consumed by the runtime to work
// around miscompile by gcc.
eImageInfo_CorrectedSynthesize = (1 << 4), // This flag is no longer set by clang.
eImageInfo_SignedClassRO = (1 << 4), // Reused (was _CorrectedSynthesize)
eImageInfo_ImageIsSimulated = (1 << 5),
eImageInfo_ClassProperties = (1 << 6)
};
@ -5036,6 +5035,17 @@ void CGObjCCommonMac::EmitImageInfo() {
// Indicate whether we are generating class properties.
Mod.addModuleFlag(llvm::Module::Error, "Objective-C Class Properties",
eImageInfo_ClassProperties);
// Indicate whether we want enforcement of pointer signing for class_ro_t
// pointers.
if (CGM.getLangOpts().PointerAuthObjcClassROPointers)
Mod.addModuleFlag(llvm::Module::Error,
"Objective-C Enforce ClassRO Pointer Signing",
eImageInfo_SignedClassRO);
else
Mod.addModuleFlag(llvm::Module::Error,
"Objective-C Enforce ClassRO Pointer Signing",
llvm::ConstantInt::get(Int8Ty, 0));
}
// struct objc_module {
@ -6223,11 +6233,19 @@ llvm::GlobalVariable *CGObjCNonFragileABIMac::BuildClassRoTInitializer(
methods.push_back(MD);
}
values.add(emitMethodList(ID->getObjCRuntimeNameAsString(),
(flags & NonFragileABI_Class_Meta)
? MethodListType::ClassMethods
: MethodListType::InstanceMethods,
methods));
llvm::Constant *MethListPtr = emitMethodList(
ID->getObjCRuntimeNameAsString(),
(flags & NonFragileABI_Class_Meta) ? MethodListType::ClassMethods
: MethodListType::InstanceMethods,
methods);
const PointerAuthSchema &MethListSchema =
CGM.getCodeGenOpts().PointerAuth.ObjCMethodListPointer;
if (!MethListPtr->isNullValue())
values.addSignedPointer(MethListPtr, MethListSchema, GlobalDecl(),
QualType());
else
values.add(MethListPtr);
const ObjCInterfaceDecl *OID = ID->getClassInterface();
assert(OID && "CGObjCNonFragileABIMac::BuildClassRoTInitializer");
@ -6275,15 +6293,20 @@ llvm::GlobalVariable *CGObjCNonFragileABIMac::BuildClassObject(
bool HiddenVisibility) {
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.ClassnfABITy);
values.add(IsAGV);
if (SuperClassGV) {
values.add(SuperClassGV);
} else {
const PointerAuthOptions &PointerAuthOpts = CGM.getCodeGenOpts().PointerAuth;
values.addSignedPointer(IsAGV, PointerAuthOpts.ObjCIsaPointers, GlobalDecl(),
QualType());
if (SuperClassGV)
values.addSignedPointer(SuperClassGV, PointerAuthOpts.ObjCSuperPointers,
GlobalDecl(), QualType());
else
values.addNullPointer(ObjCTypes.ClassnfABIPtrTy);
}
values.add(ObjCEmptyCacheVar);
values.add(ObjCEmptyVtableVar);
values.add(ClassRoGV);
values.addSignedPointer(ClassRoGV, PointerAuthOpts.ObjCClassROPointers,
GlobalDecl(), QualType());
llvm::GlobalVariable *GV = cast<llvm::GlobalVariable>(
GetClassGlobal(CI, isMetaclass, ForDefinition));
@ -6543,15 +6566,27 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
}
}
auto instanceMethodList = emitMethodList(
llvm::Constant *InstanceMethodList = emitMethodList(
listName, MethodListType::CategoryInstanceMethods, instanceMethods);
auto classMethodList = emitMethodList(
const PointerAuthSchema &MethListSchema =
CGM.getCodeGenOpts().PointerAuth.ObjCMethodListPointer;
if (!InstanceMethodList->isNullValue())
values.addSignedPointer(InstanceMethodList, MethListSchema, GlobalDecl(),
QualType());
else
values.add(InstanceMethodList);
llvm::Constant *ClassMethodList = emitMethodList(
listName, MethodListType::CategoryClassMethods, classMethods);
values.add(instanceMethodList);
values.add(classMethodList);
if (!ClassMethodList->isNullValue())
values.addSignedPointer(ClassMethodList, MethListSchema, GlobalDecl(),
QualType());
else
values.add(ClassMethodList);
// Keep track of whether we have actual metadata to emit.
bool isEmptyCategory =
instanceMethodList->isNullValue() && classMethodList->isNullValue();
InstanceMethodList->isNullValue() && ClassMethodList->isNullValue();
const ObjCCategoryDecl *Category =
Interface->FindCategoryDeclaration(OCD->getIdentifier());
@ -6629,7 +6664,13 @@ void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder,
} else {
llvm::Function *fn = GetMethodDefinition(MD);
assert(fn && "no definition for method?");
method.add(fn);
if (const PointerAuthSchema &Schema =
CGM.getCodeGenOpts().PointerAuth.ObjCMethodListFunctionPointers) {
llvm::Constant *Bitcast =
llvm::ConstantExpr::getBitCast(fn, ObjCTypes.Int8PtrProgramASTy);
method.addSignedPointer(Bitcast, Schema, GlobalDecl(), QualType());
} else
method.add(fn);
}
method.finishAndAddTo(builder);
@ -7672,10 +7713,15 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID,
}
llvm::Value *VTableIdx = llvm::ConstantInt::get(CGM.Int32Ty, 2);
llvm::Constant *VTablePtr = llvm::ConstantExpr::getInBoundsGetElementPtr(
VTableGV->getValueType(), VTableGV, VTableIdx);
ConstantInitBuilder builder(CGM);
auto values = builder.beginStruct(ObjCTypes.EHTypeTy);
values.add(llvm::ConstantExpr::getInBoundsGetElementPtr(
VTableGV->getValueType(), VTableGV, VTableIdx));
const PointerAuthSchema &TypeInfoSchema =
CGM.getCodeGenOpts().PointerAuth.CXXTypeInfoVTablePointer;
values.addSignedPointer(VTablePtr, TypeInfoSchema, GlobalDecl(), QualType());
values.add(GetClassName(ClassName));
values.add(GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition));

View File

@ -6616,7 +6616,9 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) {
auto Fields = Builder.beginStruct(STy);
// Class pointer.
Fields.add(cast<llvm::Constant>(CFConstantStringClassRef));
Fields.addSignedPointer(cast<llvm::Constant>(CFConstantStringClassRef),
getCodeGenOpts().PointerAuth.ObjCIsaPointers,
GlobalDecl(), QualType());
// Flags.
if (IsSwiftABI) {

View File

@ -1746,6 +1746,13 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args,
Args.addOptInFlag(CmdArgs, options::OPT_faarch64_jump_table_hardening,
options::OPT_fno_aarch64_jump_table_hardening);
Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_objc_isa,
options::OPT_fno_ptrauth_objc_isa);
Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_objc_interface_sel,
options::OPT_fno_ptrauth_objc_interface_sel);
Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_objc_class_ro,
options::OPT_fno_ptrauth_objc_class_ro);
if (Triple.getEnvironment() == llvm::Triple::PAuthTest)
handlePAuthABI(Args, CmdArgs);

View File

@ -1541,6 +1541,25 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
Key::ASIA, LangOpts.PointerAuthInitFiniAddressDiscrimination,
Discrimination::Constant, InitFiniPointerConstantDiscriminator);
}
Opts.ObjCMethodListFunctionPointers =
PointerAuthSchema(Key::ASIA, true, Discrimination::None);
Opts.ObjCMethodListPointer =
PointerAuthSchema(Key::ASDA, true, Discrimination::Constant,
MethodListPointerConstantDiscriminator);
if (LangOpts.PointerAuthObjcIsa) {
Opts.ObjCIsaPointers =
PointerAuthSchema(Key::ASDA, true, Discrimination::Constant,
IsaPointerConstantDiscriminator);
Opts.ObjCSuperPointers =
PointerAuthSchema(Key::ASDA, true, Discrimination::Constant,
SuperPointerConstantDiscriminator);
}
if (LangOpts.PointerAuthObjcClassROPointers)
Opts.ObjCClassROPointers =
PointerAuthSchema(Key::ASDA, true, Discrimination::Constant,
ClassROConstantDiscriminator);
}
Opts.ReturnAddresses = LangOpts.PointerAuthReturns;
Opts.AuthTraps = LangOpts.PointerAuthAuthTraps;
@ -3573,6 +3592,12 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts,
GenerateArg(Consumer, OPT_fptrauth_elf_got);
if (Opts.AArch64JumpTableHardening)
GenerateArg(Consumer, OPT_faarch64_jump_table_hardening);
if (Opts.PointerAuthObjcIsa)
GenerateArg(Consumer, OPT_fptrauth_objc_isa);
if (Opts.PointerAuthObjcInterfaceSel)
GenerateArg(Consumer, OPT_fptrauth_objc_interface_sel);
if (Opts.PointerAuthObjcClassROPointers)
GenerateArg(Consumer, OPT_fptrauth_objc_class_ro);
}
static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
@ -3596,6 +3621,15 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got);
Opts.AArch64JumpTableHardening =
Args.hasArg(OPT_faarch64_jump_table_hardening);
Opts.PointerAuthObjcIsa = Args.hasArg(OPT_fptrauth_objc_isa);
Opts.PointerAuthObjcClassROPointers = Args.hasArg(OPT_fptrauth_objc_class_ro);
Opts.PointerAuthObjcInterfaceSel =
Args.hasArg(OPT_fptrauth_objc_interface_sel);
if (Opts.PointerAuthObjcInterfaceSel)
Opts.PointerAuthObjcInterfaceSelKey =
static_cast<unsigned>(PointerAuthSchema::ARM8_3Key::ASDB);
}
/// Check if input file kind and language standard are compatible.

View File

@ -42,6 +42,19 @@ typedef enum {
The extra data is always 0. */
ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data,
/* The key used to sign metadata pointers to Objective-C method-lists. */
ptrauth_key_method_list_pointer = ptrauth_key_asda,
/* The key used to sign Objective-C isa and super pointers. */
ptrauth_key_objc_isa_pointer = ptrauth_key_process_independent_data,
ptrauth_key_objc_super_pointer = ptrauth_key_process_independent_data,
/* The key used to sign selector pointers */
ptrauth_key_objc_sel_pointer = ptrauth_key_process_dependent_data,
/* The key used to sign Objective-C class_ro_t pointers. */
ptrauth_key_objc_class_ro_pointer = ptrauth_key_process_independent_data,
/* The key used to sign pointers in ELF .init_array/.fini_array. */
ptrauth_key_init_fini_pointer = ptrauth_key_process_independent_code,
@ -259,6 +272,46 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
/* The value is ptrauth_string_discriminator("init_fini") */
#define __ptrauth_init_fini_discriminator 0xd9d4
/* Objective-C pointer auth ABI qualifiers */
#define __ptrauth_objc_method_list_imp \
__ptrauth(ptrauth_key_function_pointer, 1, 0)
#if __has_feature(ptrauth_objc_method_list_pointer)
#define __ptrauth_objc_method_list_pointer \
__ptrauth(ptrauth_key_method_list_pointer, 1, 0xC310)
#else
#define __ptrauth_objc_method_list_pointer
#endif
#define __ptrauth_isa_discriminator 0x6AE1
#define __ptrauth_super_discriminator 0xB5AB
#define __ptrauth_objc_isa_pointer \
__ptrauth(ptrauth_key_objc_isa_pointer, 1, __ptrauth_isa_discriminator)
#if __has_feature(ptrauth_restricted_intptr_qualifier)
#define __ptrauth_objc_isa_uintptr \
__ptrauth_restricted_intptr(ptrauth_key_objc_isa_pointer, 1, \
__ptrauth_isa_discriminator)
#else
#define __ptrauth_objc_isa_uintptr \
__ptrauth(ptrauth_key_objc_isa_pointer, 1, __ptrauth_isa_discriminator)
#endif
#define __ptrauth_objc_super_pointer \
__ptrauth(ptrauth_key_objc_super_pointer, 1, __ptrauth_super_discriminator)
#define __ptrauth_objc_sel_discriminator 0x57c2
#if __has_feature(ptrauth_objc_interface_sel)
#define __ptrauth_objc_sel \
__ptrauth(ptrauth_key_objc_sel_pointer, 1, __ptrauth_objc_sel_discriminator)
#else
#define __ptrauth_objc_sel
#endif
#define __ptrauth_objc_class_ro_discriminator 0x61f8
#define __ptrauth_objc_class_ro \
__ptrauth(ptrauth_key_objc_class_ro_pointer, 1, \
__ptrauth_objc_class_ro_discriminator)
#else
#define ptrauth_strip(__value, __key) \
@ -331,6 +384,10 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \
extra_discrimination...)
#define __ptrauth_objc_isa_pointer
#define __ptrauth_objc_isa_uintptr
#define __ptrauth_objc_super_pointer
#endif /* __has_feature(ptrauth_intrinsics) */
#endif /* __PTRAUTH_H */

View File

@ -5585,6 +5585,14 @@ Decl *SemaObjC::ActOnIvar(Scope *S, SourceLocation DeclStart, Declarator &D,
TypeSourceInfo *TInfo = SemaRef.GetTypeForDeclarator(D);
QualType T = TInfo->getType();
ASTContext &Context = getASTContext();
if (Context.getLangOpts().PointerAuthObjcInterfaceSel &&
!T.getPointerAuth()) {
if (Context.isObjCSelType(T.getUnqualifiedType())) {
if (auto PAQ = Context.getObjCMemberSelTypePtrAuth())
T = Context.getPointerAuthType(T, PAQ);
}
}
if (BitWidth) {
// 6.7.2.1p3, 6.7.2.1p4

View File

@ -1298,6 +1298,15 @@ Decl *SemaObjC::ActOnPropertyImplDecl(
}
}
if (Context.getLangOpts().PointerAuthObjcInterfaceSel &&
!PropertyIvarType.getPointerAuth()) {
if (Context.isObjCSelType(QualType(PropertyIvarType.getTypePtr(), 0))) {
if (auto PAQ = Context.getObjCMemberSelTypePtrAuth())
PropertyIvarType =
Context.getPointerAuthType(PropertyIvarType, PAQ);
}
}
Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl,
PropertyIvarLoc,PropertyIvarLoc, PropertyIvar,
PropertyIvarType, /*TInfo=*/nullptr,

View File

@ -1371,13 +1371,13 @@ void test71(void) {
// CHECK: %[[T:.*]] = alloca [2 x ptr], align 16
// CHECK: %[[V0:.*]] = call ptr @llvm.objc.retain(ptr %[[A]])
// CHECK: %[[V1:.*]] = call ptr @llvm.objc.retain(ptr %[[B]]) #2
// CHECK: %[[V3:.*]] = load ptr, ptr %[[A_ADDR]], align 8, !tbaa !7
// CHECK: %[[V3:.*]] = load ptr, ptr %[[A_ADDR]], align 8, !tbaa !{{[0-9]+}}
// CHECK: %[[V4:.*]] = call ptr @llvm.objc.retain(ptr %[[V3]]) #2
// CHECK: store ptr %[[V4]], ptr %[[T]], align 8, !tbaa !7
// CHECK: store ptr %[[V4]], ptr %[[T]], align 8, !tbaa !{{[0-9]+}}
// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds ptr, ptr %[[T]], i64 1
// CHECK: %[[V5:.*]] = load ptr, ptr %[[B_ADDR]], align 8, !tbaa !7
// CHECK: %[[V5:.*]] = load ptr, ptr %[[B_ADDR]], align 8, !tbaa !{{[0-9]+}}
// CHECK: %[[V6:.*]] = call ptr @llvm.objc.retain(ptr %[[V5]]) #2
// CHECK: store ptr %[[V6]], ptr %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !7
// CHECK: store ptr %[[V6]], ptr %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !{{[0-9]+}}
// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x ptr], ptr %[[T]], i32 0, i32 0
// CHECK: %[[V7:.*]] = getelementptr inbounds ptr, ptr %[[ARRAY_BEGIN]], i64 2

View File

@ -13,11 +13,11 @@ typedef double double4x4 __attribute__((matrix_type(4, 4)));
// CHECK-LABEL: @test_index_placeholders(
// CHECK-NEXT: entry:
// CHECK: [[IV:%.*]] = load ptr, ptr [[IV_ADDR:%.*]], align 8
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !7
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !{{[0-9]+}}
// CHECK-NEXT: [[CALL:%.*]] = call i32 @objc_msgSend(ptr noundef [[IV]], ptr noundef [[SEL]])
// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[CALL]] to i64
// CHECK-NEXT: [[IV2:%.*]] = load ptr, ptr [[IV_ADDR]], align 8
// CHECK-NEXT: [[SEL2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !7
// CHECK-NEXT: [[SEL2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !{{[0-9]+}}
// CHECK-NEXT: [[CALL1:%.*]] = call i32 @objc_msgSend(ptr noundef [[IV2]], ptr noundef [[SEL2]])
// CHECK-NEXT: [[CONV2:%.*]] = sext i32 [[CALL1]] to i64
// CHECK-NEXT: [[IDX1:%.*]] = mul i64 [[CONV2]], 4
@ -38,17 +38,17 @@ __attribute__((objc_root_class))
// CHECK-LABEL: @test_base_and_index_placeholders(
// CHECK: [[IV:%.*]] = load ptr, ptr [[IV_ADDR:%.*]], align 8
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !7
// CHECK-NEXT: [[SEL:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !{{[0-9]+}}
// CHECK-NEXT: [[CALL:%.*]] = call i32 @objc_msgSend(ptr noundef [[IV]], ptr noundef [[SEL]])
// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[CALL]] to i64
// CHECK-NEXT: [[IV2:%.*]] = load ptr, ptr [[IV_ADDR]], align 8
// CHECK-NEXT: [[SEL2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !7
// CHECK-NEXT: [[SEL2:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !{{[0-9]+}}
// CHECK-NEXT: [[CALL1:%.*]] = call i32 @objc_msgSend(ptr noundef [[IV2]], ptr noundef [[SEL2]])
// CHECK-NEXT: [[CONV2:%.*]] = sext i32 [[CALL1]] to i64
// CHECK-NEXT: [[IDX1:%.*]] = mul i64 [[CONV2]], 4
// CHECK-NEXT: [[IDX2:%.*]] = add i64 [[IDX1]], [[CONV]]
// CHECK-NEXT: [[M:%.*]] = load ptr, ptr %m.addr, align 8
// CHECK-NEXT: [[SEL3:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !7
// CHECK-NEXT: [[SEL3:%.*]] = load ptr, ptr @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !{{[0-9]+}}
// CHECK-NEXT: [[MAT:%.*]] = call <16 x double> @objc_msgSend(ptr noundef [[M]], ptr noundef [[SEL3]])
// CHECK-NEXT: [[MATEXT:%.*]] = extractelement <16 x double> [[MAT]], i64 [[IDX2]]
// CHECK-NEXT: ret double [[MATEXT]]

View File

@ -0,0 +1,17 @@
// RUN: %clang_cc1 -triple arm64e -fptrauth-calls -fptrauth-vtable-pointer-address-discrimination -fptrauth-vtable-pointer-type-discrimination -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s
__attribute__((objc_root_class))
@interface Root {
Class isa;
}
@end
__attribute__((objc_exception))
@interface A : Root
@end
@implementation A
@end
// CHECK: @"OBJC_EHTYPE_$_A" = global %struct._objc_typeinfo { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), i32 2), ptr @OBJC_CLASS_NAME_, ptr @"OBJC_CLASS_$_A" }
//. @"OBJC_EHTYPE_$_A" = global %struct._objc_typeinfo { ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), ptr @OBJC_CLASS_NAME_, ptr @"OBJC_CLASS_$_A" }

View File

@ -0,0 +1,40 @@
// RUN: %clang_cc1 -fptrauth-calls -fptrauth-objc-isa -fobjc-arc -fblocks -triple arm64e -emit-llvm %s -o - | FileCheck %s
void (^globalblock)(void) = ^{};
// CHECK: [[GLOBAL_BLOCK:@.*]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr ptrauth (ptr @_NSConcreteGlobalBlock, i32 2, i64 27361, ptr [[GLOBAL_BLOCK]]), i32 1342177280, i32 0, ptr @globalblock_block_invoke, ptr @"__block_descriptor_32_e5_v8\01?0l" }, align 8 #0
@interface A
- (int) count;
@end
void use_block(int (^)(void));
// CHECK-LABEL: define dso_local void @test_block_literal(
void test_block_literal(int i) {
// CHECK: [[I:%.*]] = alloca i32,
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align
// CHECK: [[ISAPTRADDR:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 0
// CHECK-NEXT: [[ISAPTRADDR_I:%.*]] = ptrtoint ptr [[ISAPTRADDR]] to i64
// CHECK-NEXT: [[ISADISCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ISAPTRADDR_I]], i64 27361)
// CHECK-NEXT: [[SIGNEDISA:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @_NSConcreteStackBlock to i64), i32 2, i64 [[ISADISCRIMINATOR]])
// CHECK-NEXT: [[SIGNEDISAPTR:%.*]] = inttoptr i64 [[SIGNEDISA]] to ptr
// CHECK-NEXT: store ptr [[SIGNEDISAPTR]], ptr [[ISAPTRADDR]]
use_block(^{return i;});
}
void test_conversion_helper(id);
// CHECK-LABEL: define dso_local void @test_conversion(
void test_conversion(id a) {
// CHECK: [[A:%.*addr]] = alloca ptr
// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align
// CHECK: [[ISAPTRADDR:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 0
// CHECK-NEXT: [[ISAPTRADDR_I:%.*]] = ptrtoint ptr [[ISAPTRADDR]] to i64
// CHECK-NEXT: [[ISADISCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ISAPTRADDR_I]], i64 27361)
// CHECK-NEXT: [[SIGNEDISA:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @_NSConcreteStackBlock to i64), i32 2, i64 [[ISADISCRIMINATOR]])
// CHECK-NEXT: [[SIGNEDISAPTR:%.*]] = inttoptr i64 [[SIGNEDISA]] to ptr
// CHECK-NEXT: store ptr [[SIGNEDISAPTR]], ptr [[ISAPTRADDR]]
test_conversion_helper(^{
(void)a;
});
}

View File

@ -0,0 +1,24 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -Wno-objc-root-class -fptrauth-objc-class-ro -fobjc-arc -emit-llvm -o - %s | FileCheck %s
// CHECK: @"OBJC_CLASS_$_C" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C", ptr null, ptr @_objc_empty_cache, ptr null, ptr ptrauth (ptr @"_OBJC_CLASS_RO_$_C", i32 2, i64 25080, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_CLASS_$_C", i32 0, i32 4)) }, section "__DATA, __objc_data", align 8
// CHECK: @"OBJC_METACLASS_$_C" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C", ptr @"OBJC_CLASS_$_C", ptr @_objc_empty_cache, ptr null, ptr ptrauth (ptr @"_OBJC_METACLASS_RO_$_C", i32 2, i64 25080, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_METACLASS_$_C", i32 0, i32 4)) }, section "__DATA, __objc_data", align 8
// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [2 x i8] c"C\00", section "__TEXT,__objc_classname,cstring_literals", align 1
// CHECK: @"_OBJC_METACLASS_RO_$_C" = internal global %struct._class_ro_t { i32 131, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8
// CHECK: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [3 x i8] c"m0\00", section "__TEXT,__objc_methname,cstring_literals", align 1
// CHECK: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1
// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01-[C m0]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2)) }] }, section "__DATA, __objc_const", align 8
// CHECK: @"_OBJC_CLASS_RO_$_C" = internal global %struct._class_ro_t { i32 130, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_CLASS_RO_$_C", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8
// CHECK: @OBJC_SELECTOR_REFERENCES_ = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8
// CHECK: @"OBJC_LABEL_CLASS_$" = private global [1 x ptr] [ptr @"OBJC_CLASS_$_C"], section "__DATA,__objc_classlist,regular,no_dead_strip"
@interface C
- (void) m0;
@end
@implementation C
- (void)m0 {}
@end
void test_sign_class_ro(C *c) {
[c m0];
}

View File

@ -0,0 +1,103 @@
// RUN: %clang_cc1 -Wno-everything -fblocks -fptrauth-intrinsics -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fobjc-arc -O2 -disable-llvm-passes -o - %s | FileCheck %s
#if __has_feature(ptrauth_objc_signable_class)
struct TestStruct {
__ptrauth(2, 1, 1234) Class isa;
};
@interface TestClass {
@public
__ptrauth(2, 1, 1234) Class isa;
}
@end
struct TestConstStruct {
__ptrauth(2, 1, 1234) const Class isa;
__ptrauth(2, 1, 1234) volatile Class visa;
};
@interface TestConstClass {
@public
__ptrauth(2, 1, 1234) const Class isa;
__ptrauth(2, 1, 1234) volatile Class visa;
}
@end
// CHECK-LABEL: define void @setTestStructIsa(ptr %t, ptr %c) #0 {
void setTestStructIsa(struct TestStruct *t, Class c) {
t->isa = c;
// CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[C_ADDR:%.*]] = alloca ptr, align 8
// CHECK: store ptr %c, ptr [[C_ADDR]], align 8
// CHECK: [[ISA_SLOT:%.*]] = getelementptr inbounds nuw %struct.TestStruct, ptr %0, i32 0, i32 0
// CHECK: [[C:%.*]] = load ptr, ptr %c.addr, align 8
// CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr [[ISA_SLOT]] to i64
// CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234)
// CHECK: [[CAST_C:%.*]] = ptrtoint ptr [[C]] to i64
// CHECK: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_C]], i32 2, i64 [[BLENDED_VALUE]])
}
// CHECK-LABEL: define void @setTestClassIsa(ptr %t, ptr %c) #0 {
void setTestClassIsa(TestClass *t, Class c) {
t->isa = c;
// CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[C_ADDR:%.*]] = alloca ptr, align 8
// CHECK: store ptr %c, ptr [[C_ADDR]], align 8
// CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8
// CHECK: [[IVAR_OFFSET32:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestClass.isa", align 8
// CHECK: [[IVAR_OFFSET64:%.*]] = sext i32 [[IVAR_OFFSET32]] to i64
// CHECK: [[ADDED_PTR:%.*]] = getelementptr inbounds i8, ptr %1, i64 [[IVAR_OFFSET64]]
// CHECK: [[C_VALUE:%.*]] = load ptr, ptr [[C_ADDR]], align 8
// CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr [[ADDED_PTR]] to i64
// CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234)
// CHECK: [[CAST_C_VALUE:%.*]] = ptrtoint ptr [[C_VALUE]] to i64
// CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_C_VALUE]], i32 2, i64 [[BLENDED_VALUE]])
}
// CHECK-LABEL: define ptr @getTestStructIsa(ptr %t) #0 {
Class getTestStructIsa(struct TestStruct *t) {
return t->isa;
// CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[T_VALUE:%.*]] = load ptr, ptr [[T_ADDR]], align 8
// CHECK: [[ISA_SLOT:%.*]] = getelementptr inbounds nuw %struct.TestStruct, ptr [[T_VALUE]], i32 0, i32 0
// CHECK: [[ISA_VALUE:%.*]] = load ptr, ptr [[ISA_SLOT]], align 8
// CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr %isa to i64
// CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234)
// CHECK: [[CAST_ISA_VALUE:%.*]] = ptrtoint ptr [[ISA_VALUE]] to i64
// CHECK: [[SIGNED_VALUE:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_ISA_VALUE]], i32 2, i64 [[BLENDED_VALUE]])
}
// CHECK-LABEL: define ptr @getTestClassIsa(ptr %t) #0 {
Class getTestClassIsa(TestClass *t) {
return t->isa;
// CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8
// CHECK: [[IVAR:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestClass.isa", align 8
// CHECK: [[IVAR_CONV:%.*]] = sext i32 [[IVAR]] to i64
// CHECK: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[T]], i64 [[IVAR_CONV]]
// CHECK: [[LOADED_VALUE:%.*]] = load ptr, ptr [[ADD_PTR]], align 8
// CHECK: [[INT_VALUE:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64
// CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[INT_VALUE]], i64 1234)
// CHECK: [[NULL_CHECK:%.*]] = icmp ne ptr [[LOADED_VALUE]], null
// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[LOADED_VALUE]] to i64
// CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 [[BLENDED_VALUE]])
}
// Just enough to verify we do actually authenticate qualified Class
// CHECK: define ptr @getTestConstClassIsa(ptr %t) #0 {
Class getTestConstClassIsa(TestConstClass *t) {
return t->isa;
// CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8
// CHECK: [[IVAR:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestConstClass.isa", align 8
// CHECK: [[IVAR_CONV:%.*]] = sext i32 [[IVAR]] to i64
// CHECK: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[T]], i64 [[IVAR_CONV]]
// CHECK: [[LOADED_VALUE:%.*]] = load ptr, ptr [[ADD_PTR]], align 8
// CHECK: [[INT_VALUE:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64
// CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[INT_VALUE]], i64 1234)
// CHECK: [[NULL_CHECK:%.*]] = icmp ne ptr [[LOADED_VALUE]], null
// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[LOADED_VALUE]] to i64
// CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 [[BLENDED_VALUE]])
}
#endif

View File

@ -0,0 +1,130 @@
// RUN: %clang_cc1 -O0 -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -nostdsysteminc -triple arm64-apple-ios -emit-llvm -fptrauth-objc-interface-sel -o - %s | FileCheck --check-prefix=CHECK-AUTHENTICATED-SEL %s
// RUN: %clang_cc1 -O0 -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -nostdsysteminc -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-UNAUTHENTICATED-SEL %s
#include <ptrauth.h>
#define __ptrauth_objc_sel_override \
__ptrauth(ptrauth_key_objc_sel_pointer, 1, 22467)
@interface Test {
@public
SEL auto_sel;
@public
const SEL const_auto_sel;
@public
volatile SEL volatile_auto_sel;
@public
SEL __ptrauth_objc_sel_override manual;
@public
const SEL __ptrauth_objc_sel_override const_manual;
@public
volatile SEL __ptrauth_objc_sel_override volatile_manual;
@public
SEL __ptrauth_objc_sel_override _manual_sel_property;
}
@property SEL auto_sel_property;
@property const SEL const_auto_sel_property;
@property volatile SEL volatile_auto_sel_property;
@property SEL manual_sel_property;
@end
// CHECK-AUTHENTICATED-SEL-LABEL: define internal ptr @"\01-[Test test:]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 {{%.*}}, i32 3, i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL: {{%.*}} = load volatile ptr, ptr {{%.*}}, align 8
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 {{%.*}}, i32 3, i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22467)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = ptrtoint ptr {{%.*}} to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22467)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = ptrtoint ptr {{%.*}} to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 {{%.*}}, i32 3, i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal ptr @"\01-[Test auto_sel_property]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal void @"\01-[Test setAuto_sel_property:]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal ptr @"\01-[Test const_auto_sel_property]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal void @"\01-[Test setConst_auto_sel_property:]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal ptr @"\01-[Test volatile_auto_sel_property]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL-LABEL: define internal void @"\01-[Test setVolatile_auto_sel_property:]"
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 {{%.*}}, i32 3, i64 {{%.*}})
@implementation Test
- (SEL)test:(Test *)in {
_auto_sel_property = in->_auto_sel_property;
_volatile_auto_sel_property = in->_volatile_auto_sel_property;
_manual_sel_property = in->_manual_sel_property;
return _const_auto_sel_property;
}
@end
void auto_sel(Test *out, Test *in) {
out->auto_sel = in->auto_sel;
}
// CHECK-AUTHENTICATED-SEL-LABEL: define void @auto_sel
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
// CHECK-UNAUTHENTICATED-SEL-LABEL: define void @auto_sel
SEL const_auto_sel(Test *in) {
return in->const_auto_sel;
}
// CHECK-AUTHENTICATED-SEL-LABEL: define ptr @const_auto_sel
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = ptrtoint ptr {{%.*}} to i64
// CHECK-AUTHENTICATED-SEL: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL: [[RESULT:%.*]] = inttoptr i64 [[AUTHENTICATED]] to ptr
void volatile_auto_sel(Test *out, Test *in) {
out->volatile_auto_sel = in->volatile_auto_sel;
}
// CHECK-AUTHENTICATED-SEL-LABEL: define void @volatile_auto_sel
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
void manual(Test *out, Test *in) {
out->manual = in->manual;
}
// CHECK-AUTHENTICATED-SEL-LABEL: define void @manual
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22467)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22467)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
// CHECK-UNAUTHENTICATED-SEL-LABEL: define void @manual
// CHECK-UNAUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22467)
// CHECK-UNAUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-UNAUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22467)
// CHECK-UNAUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-UNAUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])

View File

@ -0,0 +1,57 @@
// RUN: %clang_cc1 -I %S/Inputs -fptrauth-calls -fptrauth-objc-isa -triple arm64-apple-ios -emit-llvm -no-enable-noundef-analysis -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s
#include "literal-support.h"
#if __has_feature(objc_bool)
#define YES __objc_yes
#define NO __objc_no
#else
#define YES ((BOOL)1)
#define NO ((BOOL)0)
#endif
@class NSString;
// CHECK: @"OBJC_METACLASS_$_C" = global %struct._class_t { ptr ptrauth (ptr @"OBJC_METACLASS_$_Base", i32 2, i64 27361, ptr @"OBJC_METACLASS_$_C"), ptr ptrauth (ptr @"OBJC_METACLASS_$_Base", i32 2, i64 46507, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_METACLASS_$_C", i32 0, i32 1)), ptr @_objc_empty_cache, ptr null, ptr @"_OBJC_METACLASS_RO_$_C" }
// CHECK: @"OBJC_CLASSLIST_SUP_REFS_$_" = private global ptr @"OBJC_METACLASS_$_C"
// CHECK: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [5 x i8] c"test\00"
// CHECK: @OBJC_SELECTOR_REFERENCES_ = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_
// CHECK: @"OBJC_METACLASS_$_Base" = external global %struct._class_t
// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [2 x i8] c"C\00"
// CHECK: @OBJC_METH_VAR_NAME_.1 = private unnamed_addr constant [11 x i8] c"super_test\00"
// CHECK: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00"
// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.1, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01+[C super_test]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2)) }] }
// CHECK: @"_OBJC_METACLASS_RO_$_C" = internal global %struct._class_ro_t { i32 129, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr @"_OBJC_$_CLASS_METHODS_C", i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_METACLASS_RO_$_C", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }
// CHECK: @"OBJC_CLASS_$_Base" = external global %struct._class_t
// CHECK: @"_OBJC_CLASS_RO_$_C" = internal global %struct._class_ro_t { i32 128, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }
// @"_OBJC_CLASS_RO_$_C" = internal global %struct._class_ro_t { i32 128, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr null, i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_CLASS_RO_$_C", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }
// CHECK: @"OBJC_CLASS_$_C" = global %struct._class_t { ptr ptrauth (ptr @"OBJC_METACLASS_$_C", i32 2, i64 27361, ptr @"OBJC_CLASS_$_C"), ptr ptrauth (ptr @"OBJC_CLASS_$_Base", i32 2, i64 46507, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_CLASS_$_C", i32 0, i32 1)), ptr @_objc_empty_cache, ptr null, ptr @"_OBJC_CLASS_RO_$_C" }
// CHECK: @"OBJC_LABEL_CLASS_$" = private global [1 x ptr] [ptr @"OBJC_CLASS_$_C"]
@interface Base
+ (void)test;
@end
@interface C : Base
@end
@implementation C
// CHECK-LABEL: define internal void @"\01+[C super_test]"(ptr %self, ptr %_cmd) #1 {
+ (void)super_test {
return [super test];
// CHECK: [[SELF_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[CMD_ADDR:%.*]] = alloca ptr, align 8
// CHECK: [[SUPER_STRUCT:%.*]] = alloca %struct._objc_super, align 8
// CHECK: store ptr %self, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}}
// CHECK: store ptr %_cmd, ptr [[CMD_ADDR]], align 8, !tbaa !{{[0-9]+}}
// CHECK: [[TARGET:%.*]] = load ptr, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}}
// CHECK: [[OBJC_SUPER_TARGET:%.*]] = getelementptr inbounds nuw %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 0
// CHECK: store ptr [[TARGET]], ptr [[OBJC_SUPER_TARGET]], align 8
// CHECK: [[SUPER_REFERENCES:%.*]] = load ptr, ptr @"OBJC_CLASSLIST_SUP_REFS_$_"
// CHECK: [[OBJC_SUPER_SUPER:%.*]] = getelementptr inbounds nuw %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 1
// CHECK: store ptr [[SUPER_REFERENCES]], ptr [[OBJC_SUPER_SUPER:%.*]], align 8
// CHECK: call void @objc_msgSendSuper2(ptr %objc_super, ptr %4)
}
@end
id str = @"";

View File

@ -0,0 +1,17 @@
// RUN: %clang_cc1 -Wno-objc-root-class -fptrauth-calls -triple arm64e -fptrauth-objc-class-ro %s -emit-llvm -o - | FileCheck %s
@interface X
@end
@implementation X
-(void)meth {}
@end
// CHECK: @"OBJC_CLASS_$_X" = global %struct._class_t { ptr @"OBJC_METACLASS_$_X", ptr null, ptr @_objc_empty_cache, ptr null, ptr ptrauth (ptr @"_OBJC_CLASS_RO_$_X", i32 2, i64 25080, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_CLASS_$_X", i32 0, i32 4)) }
// CHECK: @"OBJC_METACLASS_$_X" = global %struct._class_t { ptr @"OBJC_METACLASS_$_X", ptr @"OBJC_CLASS_$_X", ptr @_objc_empty_cache, ptr null, ptr ptrauth (ptr @"_OBJC_METACLASS_RO_$_X", i32 2, i64 25080, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_METACLASS_$_X", i32 0, i32 4)) }
// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [2 x i8] c"X\00"
// CHECK: @"_OBJC_METACLASS_RO_$_X" = private global %struct._class_ro_t { i32 3, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }
// CHECK: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [5 x i8] c"meth\00"
// CHECK: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00"
// CHECK: @"_OBJC_$_INSTANCE_METHODS_X" = private global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01-[X meth]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_X", i32 0, i32 2, i32 0, i32 2)) }] }
// CHECK: @"_OBJC_CLASS_RO_$_X" = private global %struct._class_ro_t { i32 2, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr @"_OBJC_$_INSTANCE_METHODS_X", i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_CLASS_RO_$_X", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }
// CHECK: @"OBJC_LABEL_CLASS_$" = private global [1 x ptr] [ptr @"OBJC_CLASS_$_X"]

View File

@ -0,0 +1,80 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fptrauth-intrinsics -o - %s | FileCheck %s
typedef void (*func)();
__attribute__((objc_root_class))
@interface Root {
Class isa;
void *__ptrauth(1, 1, 1) _field1;
void *__ptrauth(1, 1, 1) _field2;
func __ptrauth(1, 1, 1) _field3;
func __ptrauth(1, 1, 123) _field4;
}
@property void *field1;
@property(nonatomic) void *field2;
@property func field3;
@property(nonatomic) func field4;
@end
@implementation Root
@end
// CHECK-LABEL: define internal ptr @"\01-[Root field1]"
// CHECK: [[LOAD:%.*]] = load atomic i64, ptr [[ADDR:%.*]] unordered
// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1)
// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[LOAD]], i32 1, i64 [[BLEND]])
// CHECK-LABEL: define internal void @"\01-[Root setField1:]"
// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR:%.*]] to i64
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1)
// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[VALUE:%.*]], i32 1, i64 [[BLEND]])
// CHECK: [[PHI:%.*]] = phi i64 [ 0, {{%.*}} ], [ [[RESULT]], {{%.*}} ]
// CHECK: store atomic i64 [[PHI]], ptr [[ADDR]] unordered
// CHECK-LABEL: define internal ptr @"\01-[Root field2]"
// CHECK: load ptr, ptr
// CHECK: [[LOAD:%.*]] = load ptr, ptr [[ADDR:%.*]],
// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR:%.*]], i64 1)
// CHECK: [[VALUE:%.*]] = ptrtoint ptr [[LOAD]] to i64
// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLEND]])
// CHECK-LABEL: define internal void @"\01-[Root setField2:]"
// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR:%.*]] to i64
// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1)
// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[VALUE:%.*]] to i64
// CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_VALUE]], i32 1, i64 [[BLEND]])
// CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr
// CHECK: [[PHI:%.*]] = phi ptr [ null, {{%.*}} ], [ [[RESULT]], {{%.*}} ]
// CHECK: store ptr [[PHI]], ptr [[ADDR]]
// CHECK-LABEL: define internal ptr @"\01-[Root field3]"
// CHECK: [[VALUE:%.*]] = load atomic i64, ptr [[ADDR:%.*]] unordered, align 8
// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 1)
// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 1, i64 [[BLENDED]], i32 0, i64 0
// CHECK-LABEL: define internal void @"\01-[Root setField3:]"
// CHECK: [[VALUE:%.*]] = load i64, ptr {{%.*}}, align 8
// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr {{%.*}} to i64
// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 1)
// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 0, i64 0, i32 1, i64 [[BLENDED]])
// CHECK: store atomic i64
// CHECK-LABEL: define internal ptr @"\01-[Root field4]"
// CHECK: load ptr, ptr
// CHECK: [[VALUE:%.*]] = load ptr, ptr [[ADDR:%.*]],
// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 123)
// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[VALUE]] to i64
// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[CAST_VALUE]], i32 1, i64 [[BLENDED]], i32 0, i64 0)
// CHECK-LABEL: define internal void @"\01-[Root setField4:]"
// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr {{%.*}} to i64
// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 123)
// CHECK: resign.nonnull:
// CHECK: [[VALUE:%.*]] = ptrtoint ptr %1 to i64
// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 0, i64 0, i32 1, i64 [[BLENDED]])

View File

@ -0,0 +1,90 @@
// RUN: %clang_cc1 -O0 -Wobjc-root-class -fptrauth-intrinsics -fptrauth-calls -nostdsysteminc -triple arm64e-apple-ios -emit-llvm -fptrauth-objc-interface-sel -o - %s | FileCheck --check-prefix=CHECK-AUTHENTICATED-SEL %s
// RUN: %clang_cc1 -O0 -Wobjc-root-class -fptrauth-intrinsics -fptrauth-calls -nostdsysteminc -triple arm64e-apple-ios -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-UNAUTHENTICATED-SEL %s
#include <ptrauth.h>
#define __ptrauth_objc_sel_override \
__ptrauth(ptrauth_key_objc_sel_pointer, 1, 22467)
extern "C" {
@interface Test {
@public
SEL auto_sel;
@public
const SEL const_auto_sel;
@public
volatile SEL volatile_auto_sel;
@public
SEL __ptrauth_objc_sel_override manual;
@public
const SEL __ptrauth_objc_sel_override const_manual;
@public
volatile SEL __ptrauth_objc_sel_override volatile_manual;
}
@end
#if __has_feature(ptrauth_objc_interface_sel)
typedef const SEL __ptrauth_objc_sel const_auto_sel_ptr_type;
const_auto_sel_ptr_type *const_auto_sel_ptr_type_test;
typedef volatile SEL __ptrauth_objc_sel volatile_auto_sel_ptr_type;
volatile_auto_sel_ptr_type *volatile_auto_sel_ptr_type_test;
#else
typedef const SEL const_auto_sel_ptr_type;
const_auto_sel_ptr_type *const_auto_sel_ptr_type_test;
typedef volatile SEL volatile_auto_sel_ptr_type;
volatile_auto_sel_ptr_type *volatile_auto_sel_ptr_type_test;
#endif
void auto_sel(Test *out, Test *in) {
out->auto_sel = in->auto_sel;
}
// CHECK-AUTHENTICATED-SEL: define void @auto_sel
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
// CHECK-UNAUTHENTICATED-SEL: define void @auto_sel
SEL const_auto_sel(Test *in) {
const_auto_sel_ptr_type_test = &in->const_auto_sel;
return in->const_auto_sel;
}
// CHECK-AUTHENTICATED-SEL: define ptr @const_auto_sel
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.blend(i64 {{%.*}}, i64 22466)
// CHECK-AUTHENTICATED-SEL: {{%.*}} = ptrtoint ptr {{%.*}} to i64
// CHECK-AUTHENTICATED-SEL: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 3, i64 {{%.*}})
// CHECK-AUTHENTICATED-SEL: [[RESULT:%.*]] = inttoptr i64 [[AUTHENTICATED]] to ptr
void volatile_auto_sel(Test *out, Test *in) {
volatile_auto_sel_ptr_type_test = &in->volatile_auto_sel;
out->volatile_auto_sel = in->volatile_auto_sel;
}
// CHECK-AUTHENTICATED-SEL: define void @volatile_auto_sel
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22466)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
void manual(Test *out, Test *in) {
out->manual = in->manual;
}
// CHECK-AUTHENTICATED-SEL: define void @manual
// CHECK-AUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22467)
// CHECK-AUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22467)
// CHECK-AUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-AUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
// CHECK-UNAUTHENTICATED-SEL: define void @manual
// CHECK-UNAUTHENTICATED-SEL: [[DST_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_DST_ADDR:%.*]], i64 22467)
// CHECK-UNAUTHENTICATED-SEL: [[CAST_SRC_ADDR:%.*]] = ptrtoint ptr [[SRC_ADDR:%.*]] to i64
// CHECK-UNAUTHENTICATED-SEL: [[SRC_DESCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_SRC_ADDR]], i64 22467)
// CHECK-UNAUTHENTICATED-SEL: [[SRC_SEL:%.*]] = ptrtoint ptr [[SRC_SEL_ADDR:%.*]] to i64
// CHECK-UNAUTHENTICATED-SEL: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[SRC_SEL]], i32 3, i64 [[DST_DESCRIMINATOR]], i32 3, i64 [[SRC_DESCRIMINATOR]])
}

View File

@ -0,0 +1,46 @@
// RUN: %clang_cc1 -fblocks -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -verify %s
#if __has_feature(ptrauth_objc_signable_class)
@class TestClass;
typedef TestClass *ClassPtr;
typedef void(^BlockPtr)();
@interface TestClass {
@public
__ptrauth(2, 1, 1) Class a;
__ptrauth(2, 1, 3) volatile Class vi;
__ptrauth(2, 1, 3) const Class ci;
__ptrauth(2, 1, 1) id b;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'id' is invalid}}
__ptrauth(2, 1, 2) ClassPtr c;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'ClassPtr' (aka 'TestClass *') is invalid}}
__ptrauth(2, 1, 2) BlockPtr d;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'BlockPtr' (aka 'void (^)()') is invalid}}
}
struct TestStruct {
__ptrauth(2, 1, 3) Class e;
__ptrauth(2, 1, 3) volatile Class vi;
__ptrauth(2, 1, 3) const Class ci;
__ptrauth(2, 1, 4) id f;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'id' is invalid}}
__ptrauth(2, 1, 5) ClassPtr g;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'ClassPtr' (aka 'TestClass *') is invalid}}
__ptrauth(2, 1, 2) BlockPtr h;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'BlockPtr' (aka 'void (^)()') is invalid}}
};
@end
void foo() {
__ptrauth(2, 1, 3) Class i;
__ptrauth(2, 1, 3) volatile Class vi;
__ptrauth(2, 1, 3) const Class ci = 0;
__ptrauth(2, 1, 4) id j;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'id' is invalid}}
__ptrauth(2, 1, 5) ClassPtr k;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'ClassPtr' (aka 'TestClass *') is invalid}}
__ptrauth(2, 1, 2) BlockPtr l;
// expected-error@-1 {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'BlockPtr' (aka 'void (^)()') is invalid}}
}
#endif

View File

@ -18,12 +18,24 @@
@property void *__ptrauth(1, 0, 1) invalid2;
// expected-error@-1 {{property may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,0,1)'}}
@property unsigned long long __ptrauth(1, 1, 1) invalid3;
// expected-error@-1 {{property may not be qualified with '__ptrauth'; type is '__ptrauth(1,1,1) unsigned long long'}}
@property unsigned long long __ptrauth(1, 0, 1) invalid4;
// expected-error@-1 {{property may not be qualified with '__ptrauth'; type is '__ptrauth(1,0,1) unsigned long long'}}
- (void *__ptrauth(1, 1, 1))invalid5;
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,1,1)'}}
- (void *__ptrauth(1, 0, 1))invalid6;
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,0,1)'}}
- (unsigned long long __ptrauth(1, 1, 1))invalid7;
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is '__ptrauth(1,1,1) unsigned long long'}}
- (unsigned long long __ptrauth(1, 0, 1))invalid8;
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is '__ptrauth(1,0,1) unsigned long long'}}
- (void)invalid9:(void *__ptrauth(1, 1, 1))a;
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,1,1)'}}
// expected-note@-2 {{method 'invalid9:' declared here}}
@ -32,10 +44,17 @@
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,0,1)'}}
// expected-note@-2 {{method 'invalid10:' declared here}}
- (void)invalid11:(unsigned long long __ptrauth(1, 1, 1))a;
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is '__ptrauth(1,1,1) unsigned long long'}}
// expected-note@-2 {{method 'invalid11:' declared here}}
- (void)invalid12:(unsigned long long __ptrauth(1, 0, 1))a;
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is '__ptrauth(1,0,1) unsigned long long'}}
// expected-note@-2 {{method 'invalid12:' declared here}}
@end
@implementation Foo
// expected-warning@-1 2{{method definition for}}
// expected-warning@-1 4{{method definition for}}
- (void *__ptrauth(1, 1, 1))invalid13 {
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,1,1)'}}
@ -47,6 +66,16 @@
return 0;
}
- (unsigned long long __ptrauth(1, 1, 1))invalid15 {
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is '__ptrauth(1,1,1) unsigned long long'}}
return 0;
}
- (unsigned long long __ptrauth(1, 0, 1))invalid16 {
// expected-error@-1 {{return type may not be qualified with '__ptrauth'; type is '__ptrauth(1,0,1) unsigned long long'}}
return 0;
}
- (void)invalid17:(void *__ptrauth(1, 1, 1))a {
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,1,1)'}}
}
@ -55,4 +84,12 @@
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is 'void *__ptrauth(1,0,1)'}}
}
- (void)invalid19:(unsigned long long __ptrauth(1, 1, 1))a {
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is '__ptrauth(1,1,1) unsigned long long'}}
}
- (void)invalid20:(unsigned long long __ptrauth(1, 0, 1))a {
// expected-error@-1 {{parameter type may not be qualified with '__ptrauth'; type is '__ptrauth(1,0,1) unsigned long long'}}
}
@end