#include "clang/Sema/SemaBPF.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/APSInt.h"
#include <optional>
namespace clang {
SemaBPF::SemaBPF(Sema &S) : SemaBase(S) {}
static bool isValidPreserveFieldInfoArg(Expr *Arg) {
if (Arg->getType()->getAsPlaceholderType())
return false;
return (Arg->IgnoreParens()->getObjectKind() == OK_BitField ||
isa<MemberExpr>(Arg->IgnoreParens()) ||
isa<ArraySubscriptExpr>(Arg->IgnoreParens()));
}
static bool isValidPreserveTypeInfoArg(Expr *Arg) {
QualType ArgType = Arg->getType();
if (ArgType->getAsPlaceholderType())
return false;
if (!isa<DeclRefExpr>(Arg->IgnoreParens()) &&
!isa<UnaryOperator>(Arg->IgnoreParens()))
return false;
if (ArgType->getAs<TypedefType>())
return true;
const Type *Ty = ArgType->getUnqualifiedDesugaredType();
if (const auto *RT = Ty->getAs<RecordType>()) {
if (!RT->getDecl()->getDeclName().isEmpty())
return true;
} else if (const auto *ET = Ty->getAs<EnumType>()) {
if (!ET->getDecl()->getDeclName().isEmpty())
return true;
}
return false;
}
static bool isValidPreserveEnumValueArg(Expr *Arg) {
QualType ArgType = Arg->getType();
if (ArgType->getAsPlaceholderType())
return false;
const auto *UO = dyn_cast<UnaryOperator>(Arg->IgnoreParens());
if (!UO)
return false;
const auto *CE = dyn_cast<CStyleCastExpr>(UO->getSubExpr());
if (!CE)
return false;
if (CE->getCastKind() != CK_IntegralToPointer &&
CE->getCastKind() != CK_NullToPointer)
return false;
const auto *DR = dyn_cast<DeclRefExpr>(CE->getSubExpr());
if (!DR)
return false;
const EnumConstantDecl *Enumerator =
dyn_cast<EnumConstantDecl>(DR->getDecl());
if (!Enumerator)
return false;
const Type *Ty = ArgType->getUnqualifiedDesugaredType();
const auto *ET = Ty->getAs<EnumType>();
if (!ET)
return false;
return llvm::is_contained(ET->getDecl()->enumerators(), Enumerator);
}
bool SemaBPF::CheckBPFBuiltinFunctionCall(unsigned BuiltinID,
CallExpr *TheCall) {
assert((BuiltinID == BPF::BI__builtin_preserve_field_info ||
BuiltinID == BPF::BI__builtin_btf_type_id ||
BuiltinID == BPF::BI__builtin_preserve_type_info ||
BuiltinID == BPF::BI__builtin_preserve_enum_value) &&
"unexpected BPF builtin");
ASTContext &Context = getASTContext();
if (SemaRef.checkArgCount(TheCall, 2))
return true;
Expr *Arg = TheCall->getArg(1);
std::optional<llvm::APSInt> Value = Arg->getIntegerConstantExpr(Context);
diag::kind kind;
if (!Value) {
if (BuiltinID == BPF::BI__builtin_preserve_field_info)
kind = diag::err_preserve_field_info_not_const;
else if (BuiltinID == BPF::BI__builtin_btf_type_id)
kind = diag::err_btf_type_id_not_const;
else if (BuiltinID == BPF::BI__builtin_preserve_type_info)
kind = diag::err_preserve_type_info_not_const;
else
kind = diag::err_preserve_enum_value_not_const;
Diag(Arg->getBeginLoc(), kind) << 2 << Arg->getSourceRange();
return true;
}
Arg = TheCall->getArg(0);
bool InvalidArg = false;
bool ReturnUnsignedInt = true;
if (BuiltinID == BPF::BI__builtin_preserve_field_info) {
if (!isValidPreserveFieldInfoArg(Arg)) {
InvalidArg = true;
kind = diag::err_preserve_field_info_not_field;
}
} else if (BuiltinID == BPF::BI__builtin_preserve_type_info) {
if (!isValidPreserveTypeInfoArg(Arg)) {
InvalidArg = true;
kind = diag::err_preserve_type_info_invalid;
}
} else if (BuiltinID == BPF::BI__builtin_preserve_enum_value) {
if (!isValidPreserveEnumValueArg(Arg)) {
InvalidArg = true;
kind = diag::err_preserve_enum_value_invalid;
}
ReturnUnsignedInt = false;
} else if (BuiltinID == BPF::BI__builtin_btf_type_id) {
ReturnUnsignedInt = false;
}
if (InvalidArg) {
Diag(Arg->getBeginLoc(), kind) << 1 << Arg->getSourceRange();
return true;
}
if (ReturnUnsignedInt)
TheCall->setType(Context.UnsignedIntTy);
else
TheCall->setType(Context.UnsignedLongTy);
return false;
}
void SemaBPF::handlePreserveAIRecord(RecordDecl *RD) {
for (auto *D : RD->decls()) {
if (D->hasAttr<BPFPreserveAccessIndexAttr>())
continue;
D->addAttr(BPFPreserveAccessIndexAttr::CreateImplicit(getASTContext()));
if (auto *Rec = dyn_cast<RecordDecl>(D))
handlePreserveAIRecord(Rec);
}
}
void SemaBPF::handlePreserveAccessIndexAttr(Decl *D, const ParsedAttr &AL) {
auto *Rec = cast<RecordDecl>(D);
handlePreserveAIRecord(Rec);
Rec->addAttr(::new (getASTContext())
BPFPreserveAccessIndexAttr(getASTContext(), AL));
}
}