@@ -3140,6 +3140,285 @@ ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) {
3140
3140
return llvm::getPointerAuthStableSipHash(Str);
3141
3141
}
3142
3142
3143
+ /// Encode a function type for use in the discriminator of a function pointer
3144
+ /// type. We can't use the itanium scheme for this since C has quite permissive
3145
+ /// rules for type compatibility that we need to be compatible with.
3146
+ ///
3147
+ /// Formally, this function associates every function pointer type T with an
3148
+ /// encoded string E(T). Let the equivalence relation T1 ~ T2 be defined as
3149
+ /// E(T1) == E(T2). E(T) is part of the ABI of values of type T. C type
3150
+ /// compatibility requires equivalent treatment under the ABI, so
3151
+ /// CCompatible(T1, T2) must imply E(T1) == E(T2), that is, CCompatible must be
3152
+ /// a subset of ~. Crucially, however, it must be a proper subset because
3153
+ /// CCompatible is not an equivalence relation: for example, int[] is compatible
3154
+ /// with both int[1] and int[2], but the latter are not compatible with each
3155
+ /// other. Therefore this encoding function must be careful to only distinguish
3156
+ /// types if there is no third type with which they are both required to be
3157
+ /// compatible.
3158
+ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
3159
+ raw_ostream &OS, QualType QT) {
3160
+ // FIXME: Consider address space qualifiers.
3161
+ const Type *T = QT.getCanonicalType().getTypePtr();
3162
+
3163
+ // FIXME: Consider using the C++ type mangling when we encounter a construct
3164
+ // that is incompatible with C.
3165
+
3166
+ switch (T->getTypeClass()) {
3167
+ case Type::Atomic:
3168
+ return encodeTypeForFunctionPointerAuth(
3169
+ Ctx, OS, cast<AtomicType>(T)->getValueType());
3170
+
3171
+ case Type::LValueReference:
3172
+ OS << "R";
3173
+ encodeTypeForFunctionPointerAuth(Ctx, OS,
3174
+ cast<ReferenceType>(T)->getPointeeType());
3175
+ return;
3176
+ case Type::RValueReference:
3177
+ OS << "O";
3178
+ encodeTypeForFunctionPointerAuth(Ctx, OS,
3179
+ cast<ReferenceType>(T)->getPointeeType());
3180
+ return;
3181
+
3182
+ case Type::Pointer:
3183
+ // C11 6.7.6.1p2:
3184
+ // For two pointer types to be compatible, both shall be identically
3185
+ // qualified and both shall be pointers to compatible types.
3186
+ // FIXME: we should also consider pointee types.
3187
+ OS << "P";
3188
+ return;
3189
+
3190
+ case Type::ObjCObjectPointer:
3191
+ case Type::BlockPointer:
3192
+ OS << "P";
3193
+ return;
3194
+
3195
+ case Type::Complex:
3196
+ OS << "C";
3197
+ return encodeTypeForFunctionPointerAuth(
3198
+ Ctx, OS, cast<ComplexType>(T)->getElementType());
3199
+
3200
+ case Type::VariableArray:
3201
+ case Type::ConstantArray:
3202
+ case Type::IncompleteArray:
3203
+ case Type::ArrayParameter:
3204
+ // C11 6.7.6.2p6:
3205
+ // For two array types to be compatible, both shall have compatible
3206
+ // element types, and if both size specifiers are present, and are integer
3207
+ // constant expressions, then both size specifiers shall have the same
3208
+ // constant value [...]
3209
+ //
3210
+ // So since ElemType[N] has to be compatible ElemType[], we can't encode the
3211
+ // width of the array.
3212
+ OS << "A";
3213
+ return encodeTypeForFunctionPointerAuth(
3214
+ Ctx, OS, cast<ArrayType>(T)->getElementType());
3215
+
3216
+ case Type::ObjCInterface:
3217
+ case Type::ObjCObject:
3218
+ OS << "<objc_object>";
3219
+ return;
3220
+
3221
+ case Type::Enum:
3222
+ // C11 6.7.2.2p4:
3223
+ // Each enumerated type shall be compatible with char, a signed integer
3224
+ // type, or an unsigned integer type.
3225
+ //
3226
+ // So we have to treat enum types as integers.
3227
+ return encodeTypeForFunctionPointerAuth(
3228
+ Ctx, OS, cast<EnumType>(T)->getDecl()->getIntegerType());
3229
+
3230
+ case Type::FunctionNoProto:
3231
+ case Type::FunctionProto: {
3232
+ // C11 6.7.6.3p15:
3233
+ // For two function types to be compatible, both shall specify compatible
3234
+ // return types. Moreover, the parameter type lists, if both are present,
3235
+ // shall agree in the number of parameters and in the use of the ellipsis
3236
+ // terminator; corresponding parameters shall have compatible types.
3237
+ //
3238
+ // That paragraph goes on to describe how unprototyped functions are to be
3239
+ // handled, which we ignore here. Unprototyped function pointers are hashed
3240
+ // as though they were prototyped nullary functions since thats probably
3241
+ // what the user meant. This behavior is non-conforming.
3242
+ // FIXME: If we add a "custom discriminator" function type attribute we
3243
+ // should encode functions as their discriminators.
3244
+ OS << "F";
3245
+ const auto *FuncType = cast<FunctionType>(T);
3246
+ encodeTypeForFunctionPointerAuth(Ctx, OS, FuncType->getReturnType());
3247
+ if (const auto *FPT = dyn_cast<FunctionProtoType>(FuncType)) {
3248
+ for (QualType Param : FPT->param_types()) {
3249
+ Param = Ctx.getSignatureParameterType(Param);
3250
+ encodeTypeForFunctionPointerAuth(Ctx, OS, Param);
3251
+ }
3252
+ if (FPT->isVariadic())
3253
+ OS << "z";
3254
+ }
3255
+ OS << "E";
3256
+ return;
3257
+ }
3258
+
3259
+ case Type::MemberPointer: {
3260
+ OS << "M";
3261
+ const auto *MPT = T->getAs<MemberPointerType>();
3262
+ encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0));
3263
+ encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType());
3264
+ return;
3265
+ }
3266
+ case Type::ExtVector:
3267
+ case Type::Vector:
3268
+ OS << "Dv" << Ctx.getTypeSizeInChars(T).getQuantity();
3269
+ break;
3270
+
3271
+ // Don't bother discriminating based on these types.
3272
+ case Type::Pipe:
3273
+ case Type::BitInt:
3274
+ case Type::ConstantMatrix:
3275
+ OS << "?";
3276
+ return;
3277
+
3278
+ case Type::Builtin: {
3279
+ const auto *BTy = T->getAs<BuiltinType>();
3280
+ switch (BTy->getKind()) {
3281
+ #define SIGNED_TYPE(Id, SingletonId) \
3282
+ case BuiltinType::Id: \
3283
+ OS << "i"; \
3284
+ return;
3285
+ #define UNSIGNED_TYPE(Id, SingletonId) \
3286
+ case BuiltinType::Id: \
3287
+ OS << "i"; \
3288
+ return;
3289
+ #define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id:
3290
+ #define BUILTIN_TYPE(Id, SingletonId)
3291
+ #include "clang/AST/BuiltinTypes.def"
3292
+ llvm_unreachable("placeholder types should not appear here.");
3293
+
3294
+ case BuiltinType::Half:
3295
+ OS << "Dh";
3296
+ return;
3297
+ case BuiltinType::Float:
3298
+ OS << "f";
3299
+ return;
3300
+ case BuiltinType::Double:
3301
+ OS << "d";
3302
+ return;
3303
+ case BuiltinType::LongDouble:
3304
+ OS << "e";
3305
+ return;
3306
+ case BuiltinType::Float16:
3307
+ OS << "DF16_";
3308
+ return;
3309
+ case BuiltinType::Float128:
3310
+ OS << "g";
3311
+ return;
3312
+
3313
+ case BuiltinType::Void:
3314
+ OS << "v";
3315
+ return;
3316
+
3317
+ case BuiltinType::ObjCId:
3318
+ case BuiltinType::ObjCClass:
3319
+ case BuiltinType::ObjCSel:
3320
+ case BuiltinType::NullPtr:
3321
+ OS << "P";
3322
+ return;
3323
+
3324
+ // Don't bother discriminating based on OpenCL types.
3325
+ case BuiltinType::OCLSampler:
3326
+ case BuiltinType::OCLEvent:
3327
+ case BuiltinType::OCLClkEvent:
3328
+ case BuiltinType::OCLQueue:
3329
+ case BuiltinType::OCLReserveID:
3330
+ case BuiltinType::BFloat16:
3331
+ case BuiltinType::VectorQuad:
3332
+ case BuiltinType::VectorPair:
3333
+ OS << "?";
3334
+ return;
3335
+
3336
+ // Don't bother discriminating based on these seldom-used types.
3337
+ case BuiltinType::Ibm128:
3338
+ return;
3339
+ #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
3340
+ case BuiltinType::Id: \
3341
+ return;
3342
+ #include "clang/Basic/OpenCLImageTypes.def"
3343
+ #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
3344
+ case BuiltinType::Id: \
3345
+ return;
3346
+ #include "clang/Basic/OpenCLExtensionTypes.def"
3347
+ #define SVE_TYPE(Name, Id, SingletonId) \
3348
+ case BuiltinType::Id: \
3349
+ return;
3350
+ #include "clang/Basic/AArch64SVEACLETypes.def"
3351
+ case BuiltinType::Dependent:
3352
+ llvm_unreachable("should never get here");
3353
+ case BuiltinType::AMDGPUBufferRsrc:
3354
+ case BuiltinType::WasmExternRef:
3355
+ #define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
3356
+ #include "clang/Basic/RISCVVTypes.def"
3357
+ llvm_unreachable("not yet implemented");
3358
+ }
3359
+ }
3360
+ case Type::Record: {
3361
+ const RecordDecl *RD = T->getAs<RecordType>()->getDecl();
3362
+ const IdentifierInfo *II = RD->getIdentifier();
3363
+
3364
+ // In C++, an immediate typedef of an anonymous struct or union
3365
+ // is considered to name it for ODR purposes, but C's specification
3366
+ // of type compatibility does not have a similar rule. Using the typedef
3367
+ // name in function type discriminators anyway, as we do here,
3368
+ // therefore technically violates the C standard: two function pointer
3369
+ // types defined in terms of two typedef'd anonymous structs with
3370
+ // different names are formally still compatible, but we are assigning
3371
+ // them different discriminators and therefore incompatible ABIs.
3372
+ //
3373
+ // This is a relatively minor violation that significantly improves
3374
+ // discrimination in some cases and has not caused problems in
3375
+ // practice. Regardless, it is now part of the ABI in places where
3376
+ // function type discrimination is used, and it can no longer be
3377
+ // changed except on new platforms.
3378
+
3379
+ if (!II)
3380
+ if (const TypedefNameDecl *Typedef = RD->getTypedefNameForAnonDecl())
3381
+ II = Typedef->getDeclName().getAsIdentifierInfo();
3382
+
3383
+ if (!II) {
3384
+ OS << "<anonymous_record>";
3385
+ return;
3386
+ }
3387
+ OS << II->getLength() << II->getName();
3388
+ return;
3389
+ }
3390
+ case Type::DeducedTemplateSpecialization:
3391
+ case Type::Auto:
3392
+ #define NON_CANONICAL_TYPE(Class, Base) case Type::Class:
3393
+ #define DEPENDENT_TYPE(Class, Base) case Type::Class:
3394
+ #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class:
3395
+ #define ABSTRACT_TYPE(Class, Base)
3396
+ #define TYPE(Class, Base)
3397
+ #include "clang/AST/TypeNodes.inc"
3398
+ llvm_unreachable("unexpected non-canonical or dependent type!");
3399
+ return;
3400
+ }
3401
+ }
3402
+
3403
+ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) const {
3404
+ assert(!T->isDependentType() &&
3405
+ "cannot compute type discriminator of a dependent type");
3406
+
3407
+ SmallString<256> Str;
3408
+ llvm::raw_svector_ostream Out(Str);
3409
+
3410
+ if (T->isFunctionPointerType() || T->isFunctionReferenceType())
3411
+ T = T->getPointeeType();
3412
+
3413
+ if (T->isFunctionType())
3414
+ encodeTypeForFunctionPointerAuth(*this, Out, T);
3415
+ else
3416
+ llvm_unreachable(
3417
+ "type discrimination of non-function type not implemented yet");
3418
+
3419
+ return llvm::getPointerAuthStableSipHash(Str);
3420
+ }
3421
+
3143
3422
QualType ASTContext::getObjCGCQualType(QualType T,
3144
3423
Qualifiers::GC GCAttr) const {
3145
3424
QualType CanT = getCanonicalType(T);
0 commit comments