// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnostic.h" #include "revng/Model/Processing.h" #include "revng/Model/QualifiedType.h" #include "revng/Model/Register.h" #include "revng/Model/Type.h" #include "revng/Support/Debug.h" #include "revng-c/Pipes/Ranks.h" #include "revng-c/Support/ModelHelpers.h" #include "revng-c/Support/PTMLC.h" #include "revng-c/TypeNames/ModelTypeNames.h" #include "HeaderToModel.h" using namespace model; using namespace revng; static Logger<> Log("header-to-model"); static constexpr std::string_view InputCFile = "revng-input.c"; static constexpr std::string_view PrimitiveTypeHeader = "revng-primitive-" "types.h"; static constexpr llvm::StringRef RawABIPrefix = "raw_"; static constexpr const char *ABIAnnotatePrefix = "abi:"; static constexpr size_t ABIAnnotatePrefixLength = std::char_traits::length(ABIAnnotatePrefix); static constexpr const char *RegAnnotatePrefix = "reg:"; static constexpr size_t RegAnnotatePrefixLength = std::char_traits::length(RegAnnotatePrefix); static constexpr const char *EnumAnnotatePrefix = "enum_underlying_type:"; static constexpr size_t EnumAnnotatePrefixLength = std::char_traits::length(EnumAnnotatePrefix); template concept HasCustomName = requires(const T &Element) { { Element.CustomName() } -> std::same_as; { Element.name() } -> std::same_as; }; template static void setCustomName(T &Element, llvm::StringRef NewName) { if (Element.name() != NewName) Element.CustomName() = NewName; } namespace clang { namespace tooling { class HeaderToModel : public ASTConsumer { public: HeaderToModel(TupleTree &Model, std::optional &Type, std::optional &Function, std::optional &Error, enum ImportFromCOption AnalysisOption) : Model(Model), Type(Type), Function(Function), Error(Error), AnalysisOption(AnalysisOption) { // Either one of these two should be null, since the editing features are // exclusive. revng_assert(not Type or not Function); } virtual void HandleTranslationUnit(ASTContext &Context) override; private: TupleTree &Model; std::optional &Type; std::optional &Function; std::optional &Error; enum ImportFromCOption AnalysisOption; }; class DeclVisitor : public clang::RecursiveASTVisitor { private: TupleTree &Model; ASTContext &Context; std::optional &Type; std::optional &Function; std::optional &Error; enum ImportFromCOption AnalysisOption; // These are used for reporting source location of an error, if any. unsigned CurrentLineNumber = 0; unsigned CurrentColumnNumber = 0; // Used to remember return values locations when parsing struct representing // the multi-reg return value. Represents register ID and mode::Type. using ModelType = std::pair>; using RawLocation = std::pair; std::optional> MultiRegisterReturnValue; public: explicit DeclVisitor(TupleTree &Model, ASTContext &Context, std::optional &Type, std::optional &Function, std::optional &Error, enum ImportFromCOption AnalysisOption); void run(clang::TranslationUnitDecl *TUD); bool TraverseDecl(clang::Decl *D); bool VisitFunctionDecl(const clang::FunctionDecl *FD); bool VisitRecordDecl(const clang::RecordDecl *RD); bool VisitEnumDecl(const EnumDecl *D); bool VisitTypedefDecl(const TypedefDecl *D); bool VisitFunctionPrototype(const FunctionProtoType *FP, std::optional TheABI); private: // This checks that the declaration is the one user provided as input. bool comesFromInternalFile(const clang::Decl *D); // This checks that the declaration comes from revng-primitive-types header // file. bool comesFromPrimitiveTypesHeader(const clang::RecordDecl *RD); // Set up line and column for the declaratrion. void setupLineAndColumn(const clang::Decl *D); // Handle clang's Struct type. bool handleStructType(const clang::RecordDecl *RD); // Handle clang's Union type. bool handleUnionType(const clang::RecordDecl *RD); // Convert clang::type to model::type. std::optional getOrCreatePrimitive(const BuiltinType *UnderlyingBuiltin, QualType Type); // Get model type for clang::RecordType (Struct/Unoion). std::optional getTypeForRecordType(const clang::RecordType *RecordType, const QualType &ClangType); // Get model type for clang::EnumType. std::optional getTypeForEnumType(const clang::EnumType *EnumType); std::optional getTypeByNameOrID(llvm::StringRef Name, TypeKind::Values Kind); std::optional getModelTypeForClangType(const QualType &QT); std::optional getEnumUnderlyingType(const std::string &TypeName); }; DeclVisitor::DeclVisitor(TupleTree &Model, ASTContext &Context, std::optional &Type, std::optional &Function, std::optional &Error, enum ImportFromCOption AnalysisOption) : Model(Model), Context(Context), Type(Type), Function(Function), Error(Error), AnalysisOption(AnalysisOption) { } // Parse ABI from the annotate attribute content. static std::optional getABI(llvm::StringRef ABIAnnotate) { if (not ABIAnnotate.startswith(ABIAnnotatePrefix)) return std::nullopt; return std::string(ABIAnnotate.substr(ABIAnnotatePrefixLength)); } static model::Architecture::Values getRawABIArchitecture(llvm::StringRef ABI) { revng_assert(ABI.starts_with(RawABIPrefix)); return model::Architecture::fromName(ABI.substr(RawABIPrefix.size())); } // Parse location of parameter (`reg:` or `stack`). static std::optional getLoc(llvm::StringRef Annotate) { if (Annotate == "stack") return Annotate.str(); if (not Annotate.startswith(RegAnnotatePrefix)) return std::nullopt; return std::string(Annotate.substr(RegAnnotatePrefixLength)); } // Parse enum's underlying type from the annotate attribute content. static std::optional parseEnumUnderlyingType(llvm::StringRef Annotate) { if (not Annotate.startswith(EnumAnnotatePrefix)) return std::nullopt; return std::string(Annotate.substr(EnumAnnotatePrefixLength)); } std::optional DeclVisitor::getEnumUnderlyingType(const std::string &TypeName) { auto MaybePrimitive = model::PrimitiveType::fromName(TypeName); if (not MaybePrimitive) { revng_log(Log, "Not a primitive type"); return std::nullopt; } return Model->getPrimitiveType(MaybePrimitive->PrimitiveKind(), MaybePrimitive->Size()); } std::optional DeclVisitor::getOrCreatePrimitive(const BuiltinType *UnderlyingBuiltin, QualType Type) { revng_assert(UnderlyingBuiltin); auto AsElaboratedType = Type->getAs(); if (not AsElaboratedType) { PrintingPolicy Policy(Context.getLangOpts()); std::string ErrorMessage = "revng: Builtin type `" + UnderlyingBuiltin->getName(Policy).str() + "` not allowed, please use a revng " "model::PrimitiveType instead"; Error = { ErrorMessage, CurrentLineNumber, CurrentColumnNumber }; return std::nullopt; } while (auto Typedef = AsElaboratedType->getAs()) { auto TheUnderlyingType = Typedef->getDecl()->getUnderlyingType(); if (not TheUnderlyingType->getAs()) break; AsElaboratedType = TheUnderlyingType->getAs(); } std::string TypeName = AsElaboratedType->getNamedType().getAsString(); auto MaybePrimitive = model::PrimitiveType::fromName(TypeName); if (not MaybePrimitive.has_value()) { std::string ErrorMessage = "revng: `" + AsElaboratedType->getNamedType().getAsString() + "`, please use a revng model::PrimitiveType " "instead"; Error = { ErrorMessage, CurrentLineNumber, CurrentColumnNumber }; return std::nullopt; } model::TypePath Result; switch (UnderlyingBuiltin->getKind()) { case BuiltinType::UInt128: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Unsigned, 16); } case BuiltinType::Int128: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Signed, 16); } case BuiltinType::ULongLong: case BuiltinType::ULong: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Unsigned, 8); } case BuiltinType::LongLong: case BuiltinType::Long: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Signed, 8); } case BuiltinType::WChar_U: case BuiltinType::UInt: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Unsigned, 4); } case BuiltinType::WChar_S: case BuiltinType::Char32: case BuiltinType::Int: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Signed, 4); } case BuiltinType::Char16: case BuiltinType::Short: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Signed, 2); } case BuiltinType::UShort: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Unsigned, 2); } case BuiltinType::Char_S: case BuiltinType::SChar: case BuiltinType::Char8: case BuiltinType::Bool: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Signed, 1); } case BuiltinType::Char_U: case BuiltinType::UChar: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Unsigned, 1); } case BuiltinType::Void: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Void, 0); } case BuiltinType::Float16: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Float, 2); } case BuiltinType::Float: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Float, 4); } case BuiltinType::Double: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Float, 8); } case BuiltinType::Float128: case BuiltinType::LongDouble: { return Model->getPrimitiveType(model::PrimitiveTypeKind::Float, 16); } default: { revng_log(Log, "Unable to handle a primitive type"); break; } } return std::nullopt; } std::optional DeclVisitor::getTypeByNameOrID(llvm::StringRef Name, TypeKind::Values Kind) { const bool IsStruct = Kind == model::TypeKind::StructType; const bool IsUnion = Kind == model::TypeKind::UnionType; const bool IsEnum = Kind == model::TypeKind::EnumType; const bool IsCABIFunction = Kind == model::TypeKind::CABIFunctionType; const bool IsRawFunctionType = Kind == model::TypeKind::RawFunctionType; // Find by name first. for (auto &Type : Model->Types()) { if (IsStruct and not llvm::isa(Type.get())) continue; if (IsUnion and not llvm::isa(Type.get())) continue; if (IsEnum and not llvm::isa(Type.get())) continue; if (IsCABIFunction and not llvm::isa(Type.get())) continue; if (IsRawFunctionType and not llvm::isa(Type.get())) continue; if (Type->CustomName() == Name) return Model->getTypePath(Type.get()); } size_t LocationOfID = Name.rfind("_"); if (LocationOfID != std::string::npos) { std::string ID = std::string(Name.substr(LocationOfID + 1)); uint64_t TypeID; std::istringstream TheStream(ID); TheStream >> TypeID; auto KeyType = model::Type::Key{ TypeID, Kind }; auto TheType = Model->getTypePath(KeyType); if (TheType.get()) return TheType; } return std::nullopt; } std::optional DeclVisitor::getTypeForRecordType(const clang::RecordType *RecordType, const QualType &ClangType) { revng_assert(RecordType); // Check if it is a primitive type described with a struct. if (comesFromPrimitiveTypesHeader(RecordType->getDecl())) { const TypedefType *AsTypedef = ClangType->getAs(); if (not AsTypedef) { revng_log(Log, "There should be a typedef for struct that defines the " "primitive type"); return std::nullopt; } auto TypeName = AsTypedef->getDecl()->getName(); auto MaybePrimitive = model::PrimitiveType::fromName(TypeName); revng_assert(MaybePrimitive); return Model->getPrimitiveType(MaybePrimitive->PrimitiveKind(), MaybePrimitive->Size()); } auto Name = RecordType->getDecl()->getName(); if (Name.empty()) { revng_log(Log, "Unable to find record type without name"); return std::nullopt; } if (RecordType->isStructureType()) { auto TheStructType = getTypeByNameOrID(Name, model::TypeKind::StructType); if (TheStructType) return *TheStructType; } else if (RecordType->isUnionType()) { auto TheUnionType = getTypeByNameOrID(Name, model::TypeKind::UnionType); if (TheUnionType) return *TheUnionType; } revng_log(Log, "Unable to find record type " << Name); return std::nullopt; } std::optional DeclVisitor::getTypeForEnumType(const clang::EnumType *EnumType) { revng_assert(EnumType); revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); auto EnumName = EnumType->getDecl()->getName(); if (EnumName.empty()) { revng_log(Log, "Unable to find enum type without name"); return std::nullopt; } auto TheEnumType = getTypeByNameOrID(EnumName, model::TypeKind::EnumType); if (TheEnumType) return *TheEnumType; revng_log(Log, "Unable to find enum type " << EnumName); return std::nullopt; } bool DeclVisitor::comesFromInternalFile(const clang::Decl *D) { SourceManager &SM = Context.getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); if (!Loc.isValid()) { revng_log(Log, "Invalid source location found"); return false; } StringRef TheFileName(Loc.getFilename()); // Process the new type only. if (TheFileName.contains(InputCFile)) return true; return false; } bool DeclVisitor::comesFromPrimitiveTypesHeader(const clang::RecordDecl *RD) { SourceManager &SM = Context.getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(RD->getLocation()); if (!Loc.isValid()) { revng_log(Log, "Invalid source location found"); return false; } StringRef TheFileName(Loc.getFilename()); if (TheFileName.contains(PrimitiveTypeHeader)) return true; return false; } void DeclVisitor::setupLineAndColumn(const clang::Decl *D) { SourceManager &SM = Context.getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); if (!Loc.isValid()) { revng_log(Log, "Invalid source location found"); return; } CurrentLineNumber = Loc.getLine(); CurrentColumnNumber = Loc.getColumn(); } std::optional DeclVisitor::getModelTypeForClangType(const QualType &QT) { std::optional TheTypePath; std::vector Qualifiers; if (QT.isConstQualified()) Qualifiers.push_back(Qualifier::createConst()); const BuiltinType *AsBuiltinType = QT->getAs(); if (AsBuiltinType) { TheTypePath = getOrCreatePrimitive(AsBuiltinType, QT); if (not TheTypePath) return std::nullopt; } else if (QT->isPointerType()) { auto PointerSize = getPointerSize(Model->Architecture()); Qualifiers.push_back({ Qualifier::createPointer(PointerSize) }); QualType BaseType; BaseType = QT->getAs()->getPointeeType(); // Handle pointers-to-pointers-... while (BaseType->isPointerType()) { Qualifiers.push_back({ Qualifier::createPointer(PointerSize) }); BaseType = BaseType->getAs()->getPointeeType(); } // NOTE: For Typedefs it will consider the underlying type. if (const BuiltinType *PointeeAsBuiltinType = BaseType ->getAs()) { TheTypePath = getOrCreatePrimitive(PointeeAsBuiltinType, BaseType); if (not TheTypePath) return std::nullopt; } else if (const RecordType *AsRecordType = BaseType->getAs()) { TheTypePath = getTypeForRecordType(AsRecordType, BaseType); if (not TheTypePath) return std::nullopt; } else if (const EnumType *AsEnum = BaseType->getAs()) { TheTypePath = getTypeForEnumType(AsEnum); if (not TheTypePath) return std::nullopt; } else if (const FunctionProtoType *AsFn = BaseType ->getAs()) { const TypedefType *AsTypedef = BaseType->getAs(); if (not AsTypedef) { revng_log(Log, "There should be a typedef for function type"); return std::nullopt; } auto FunctionName = AsTypedef->getDecl()->getName(); auto CABIFunctionTypeKind = model::TypeKind::CABIFunctionType; auto TheCABIFunctionType = getTypeByNameOrID(FunctionName, CABIFunctionTypeKind); auto RawFunctionTypeKind = model::TypeKind::RawFunctionType; auto TheRawFunctionType = getTypeByNameOrID(FunctionName, RawFunctionTypeKind); if (not TheCABIFunctionType and not TheRawFunctionType) { revng_log(Log, "Did not find function type in the model"); return std::nullopt; } TheTypePath = TheCABIFunctionType ? *TheCABIFunctionType : *TheRawFunctionType; } else { revng_log(Log, "Unsupported type used as pointer"); return std::nullopt; } } else if (QT->isArrayType()) { uint64_t NumberOfElements = 0; if (const auto *CAT = dyn_cast(QT)) { NumberOfElements = CAT->getSize().getZExtValue(); } else { // Here we can face clang::VariableArrayType and // clang::IncompleteArrayType. revng_log(Log, "Unsupported type used as an array"); return std::nullopt; } Qualifiers.push_back({ Qualifier::createArray(NumberOfElements) }); QualType ElementType = Context.getBaseElementType(QT); const BuiltinType *ElementAsBuiltin = ElementType->getAs(); if (ElementAsBuiltin) { TheTypePath = getOrCreatePrimitive(ElementAsBuiltin, ElementType); if (not TheTypePath) return std::nullopt; } else if (const RecordType *AsRecordType = ElementType ->getAs()) { TheTypePath = getTypeForRecordType(AsRecordType, ElementType); if (not TheTypePath) return std::nullopt; } else if (const EnumType *AsEnum = ElementType->getAs()) { TheTypePath = getTypeForEnumType(AsEnum); if (not TheTypePath) return std::nullopt; } else { revng_log(Log, "Unsupported array element type"); return std::nullopt; } } else if (const RecordType *AsRecordType = QT->getAs()) { TheTypePath = getTypeForRecordType(AsRecordType, QT); if (not TheTypePath) return std::nullopt; } else if (const EnumType *AsEnum = QT->getAs()) { TheTypePath = getTypeForEnumType(AsEnum); if (not TheTypePath) return std::nullopt; } else { revng_log(Log, "Unsupported QualType"); return std::nullopt; } revng_check(TheTypePath); QualifiedType Result(*TheTypePath, Qualifiers); return Result; } bool DeclVisitor::VisitFunctionDecl(const clang::FunctionDecl *FD) { if (not comesFromInternalFile(FD)) return true; revng_assert(FD); revng_assert(AnalysisOption == ImportFromCOption::EditFunctionPrototype); std::optional MaybeABI; std::vector AnnotateAttrs; { // May be multiple Annotate attributes. One for ABI, one for return value. std::for_each(begin(FD->getAttrs()), end(FD->getAttrs()), [&](Attr *Attribute) { if (auto *Annotation = dyn_cast(Attribute)) AnnotateAttrs.push_back(Annotation); }); for (auto *Annotate : AnnotateAttrs) { MaybeABI = getABI(Annotate->getAnnotation()); if (MaybeABI) break; } if (not MaybeABI or MaybeABI->empty()) { revng_log(Log, "Functions should have attribute annotate with abi: " "specification"); return false; } } revng_assert(MaybeABI.has_value()); bool IsRawFunctionType = MaybeABI->starts_with(RawABIPrefix); auto NewType = IsRawFunctionType ? makeType() : makeType(); if (not IsRawFunctionType) { auto TheModelABI = model::ABI::fromName(*MaybeABI); if (TheModelABI == model::ABI::Invalid) { revng_log(Log, "Invalid ABI provided"); return false; } auto FunctionType = cast(NewType.get()); FunctionType->ABI() = TheModelABI; auto TheRetClangType = FD->getReturnType(); auto TheRetType = getModelTypeForClangType(TheRetClangType); if (not TheRetType) { revng_log(Log, "Unsupported type for function return value"); return false; } FunctionType->ReturnType() = *TheRetType; // Handle params. uint32_t Index = 0; for (unsigned I = 0, N = FD->getNumParams(); I != N; ++I) { auto QT = FD->getParamDecl(I)->getType(); auto ParamType = getModelTypeForClangType(QT); if (not ParamType) { revng_log(Log, "Unsupported type for function parameter"); return false; } model::Argument &NewArgument = FunctionType->Arguments()[Index]; setCustomName(NewArgument, FD->getParamDecl(I)->getName()); NewArgument.Type() = *ParamType; ++Index; } } else { auto TheRetClangType = FD->getReturnType(); auto TheRawFunctionType = cast(NewType.get()); auto Architecture = getRawABIArchitecture(*MaybeABI); if (Architecture == model::Architecture::Invalid) { revng_log(Log, "Invalid raw abi architecture"); return false; } TheRawFunctionType->Architecture() = Architecture; auto ReturnValuesInserter = TheRawFunctionType->ReturnValues() .batch_insert(); // This represents multiple register location for return values. if (TheRetClangType->isStructureType()) { if (not MultiRegisterReturnValue) { revng_log(Log, "Return value should be already parsed"); return false; } for (auto &ReturnValue : *MultiRegisterReturnValue) { NamedTypedRegister ReturnValueReg(ReturnValue.first); ReturnValueReg.Type() = { ReturnValue.second.first, ReturnValue.second.second }; ReturnValuesInserter.insert(ReturnValueReg); } } else { // Return value as single register. std::for_each(begin(FD->getAttrs()), end(FD->getAttrs()), [&](Attr *Attribute) { if (auto *Annotation = dyn_cast(Attribute)) AnnotateAttrs.push_back(Annotation); }); std::optional ReturnValue; for (auto *Annotate : AnnotateAttrs) { ReturnValue = getLoc(Annotate->getAnnotation()); if (ReturnValue) break; } if (not ReturnValue or ReturnValue->empty()) { revng_log(Log, "Return value should have attribute annotate with reg or " "stack"); return false; } // TODO: Handle stack location. if (*ReturnValue == "stack") { revng_log(Log, "We don't support Return value on stack for now"); return false; } auto TheRetType = getModelTypeForClangType(TheRetClangType); if (not TheRetType) { revng_log(Log, "Unsupported type for function return value"); return false; } auto RegisterID = model::Register::fromCSVName(*ReturnValue, Model->Architecture()); if (RegisterID == model::Register::Invalid) { revng_log(Log, "Unsupported register location"); return false; } NamedTypedRegister ReturnValueReg(RegisterID); ReturnValueReg.Type() = *TheRetType; ReturnValuesInserter.insert(ReturnValueReg); } auto ArgumentsInserter = TheRawFunctionType->Arguments().batch_insert(); for (unsigned I = 0, N = FD->getNumParams(); I != N; ++I) { auto ParamDecl = FD->getParamDecl(I); if (ParamDecl->hasAttr()) { auto Annotate = std::find_if(begin(ParamDecl->getAttrs()), end(ParamDecl->getAttrs()), [&](Attr *Attribute) { return isa(Attribute); }); auto Loc = getLoc(cast(*Annotate)->getAnnotation()); if (not Loc or Loc->empty()) { revng_log(Log, "Parameters should have attribute annotate with reg or " "stack"); return false; } // TODO: Handle stack location. if (*Loc == "stack") { revng_log(Log, "We don't support parameters on stack for now"); return false; } auto LocationID = model::Register::fromCSVName(*Loc, Model->Architecture()); if (LocationID == model::Register::Invalid) { revng_log(Log, "Unsupported register location"); return false; } auto QT = ParamDecl->getType(); auto ParamType = getModelTypeForClangType(QT); if (not ParamType) { revng_log(Log, "Unsupported type for raw function parameter"); return false; } NamedTypedRegister ParamReg(LocationID); ParamReg.Type() = *ParamType; ArgumentsInserter.insert(ParamReg); } } } // Update the name if in the case it got changed. auto &ModelFunction = Model->Functions()[Function->Entry()]; setCustomName(ModelFunction, FD->getName()); // Clone the other stuff. ModelFunction.OriginalName() = Function->OriginalName(); ModelFunction.ExportedNames() = Function->ExportedNames(); // TODO: remember/clone StackFrameType as well. auto Prototype = Model->recordNewType(std::move(NewType)); ModelFunction.Prototype() = Prototype; return true; } bool DeclVisitor::VisitTypedefDecl(const TypedefDecl *D) { if (not comesFromInternalFile(D)) return true; revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); QualType TheType = D->getUnderlyingType(); if (auto Fn = llvm::dyn_cast(TheType)) { // Parse the ABI from annotate attribute attached to the typedef // declaration. Please do note that annotations on the parameters are not // attached, so we will use default RawFunctionType from the Model if the // abi is raw. // TODO: Should we change the annotate attached to function types to have // info about parameters in the toplevel annotate attribute attached to // the typedef itself? std::optional TheABI; if (D->hasAttr()) { auto TheAnnotateAttr = std::find_if(begin(D->getAttrs()), end(D->getAttrs()), [&](Attr *Attribute) { return isa(Attribute); }); TheABI = getABI(cast(*TheAnnotateAttr)->getAnnotation()); } if (not TheABI or TheABI->empty()) { revng_log(Log, "Unable to parse `abi:` from the annotate attribute"); return false; } return VisitFunctionPrototype(Fn, TheABI); } // Regular, non-function, typedef. auto ModelTypedefType = getModelTypeForClangType(TheType); if (not ModelTypedefType) { revng_log(Log, "Unsupported underlying type for typedef"); return false; } auto TypeTypedef = model::makeType(); if (AnalysisOption == ImportFromCOption::EditType) TypeTypedef->ID() = (*Type)->ID(); auto TheTypeTypeDef = cast(TypeTypedef.get()); TheTypeTypeDef->UnderlyingType() = *ModelTypedefType; setCustomName(*TheTypeTypeDef, D->getName()); if (AnalysisOption == ImportFromCOption::EditType) { // Remove old and add new type with the same ID. llvm::erase_if(Model->Types(), [&](UpcastablePointer &P) { return P.get()->ID() == (*Type)->ID(); }); Model->Types().insert(std::move(TypeTypedef)); } else { Model->recordNewType(std::move(TypeTypedef)); } return true; } bool DeclVisitor::VisitFunctionPrototype(const FunctionProtoType *FP, std::optional ABI) { revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); if (not ABI) { revng_log(Log, "No annotate attribute found with `abi:` information"); return false; } bool IsRawFunctionType = ABI->starts_with(RawABIPrefix); auto NewType = IsRawFunctionType ? makeType() : makeType(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = (*Type)->ID(); if (not IsRawFunctionType) { auto FunctionType = cast(NewType.get()); auto TheModelABI = model::ABI::fromName(*ABI); if (TheModelABI == model::ABI::Invalid) { revng_log(Log, "An invalid ABI found as an input"); return false; } FunctionType->ABI() = TheModelABI; auto TheRetClangType = FP->getReturnType(); auto TheRetModelType = getModelTypeForClangType(TheRetClangType); if (not TheRetModelType) { revng_log(Log, "Unsupported type for function return value"); return false; } FunctionType->ReturnType() = *TheRetModelType; // Handle params. uint32_t Index = 0; for (auto QT : FP->getParamTypes()) { auto ParamType = getModelTypeForClangType(QT); if (not ParamType) { revng_log(Log, "Unsupported type for function parameter"); return false; } model::Argument &NewArgument = FunctionType->Arguments()[Index]; NewArgument.Type() = *ParamType; ++Index; } } else { auto Architecture = getRawABIArchitecture(*ABI); if (Architecture == model::Architecture::Invalid) { revng_log(Log, "Invalid raw abi architecture"); return false; } // TODO: Since we do not have info about parameters annotation, we use // default raw function. auto TheDefaultPrototype = Model->DefaultPrototype(); auto DefaultRawType = cast(TheDefaultPrototype.get()); auto FunctionType = cast(NewType.get()); FunctionType->Architecture() = Architecture; FunctionType->Arguments() = DefaultRawType->Arguments(); FunctionType->ReturnValues() = DefaultRawType->ReturnValues(); FunctionType->PreservedRegisters() = DefaultRawType->PreservedRegisters(); FunctionType->FinalStackOffset() = DefaultRawType->FinalStackOffset(); } if (AnalysisOption == ImportFromCOption::EditType) { // Remove old and add new type with the same ID. llvm::erase_if(Model->Types(), [&](UpcastablePointer &P) { return P.get()->ID() == (*Type)->ID(); }); Model->Types().insert(std::move(NewType)); } else { Model->recordNewType(std::move(NewType)); } return true; } bool DeclVisitor::handleStructType(const clang::RecordDecl *RD) { const RecordDecl *Definition = RD->getDefinition(); auto NewType = makeType(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = (*Type)->ID(); setCustomName(*NewType, RD->getName()); auto Struct = cast(NewType.get()); uint64_t CurrentOffset = 0; // // Iterate over the struct fields // llvm::SmallVector, 4> ReturnValues; for (const FieldDecl *Field : Definition->fields()) { if (Field->isInvalidDecl()) { revng_log(Log, "Invalid declaration for a struct field"); return false; } std::optional LocationID; if (AnalysisOption == ImportFromCOption::EditFunctionPrototype) { if (not Field->hasAttr()) { revng_log(Log, "Struct field representing return value should have annotate " "attribute describing location"); return false; } auto Annotate = std::find_if(begin(Field->getAttrs()), end(Field->getAttrs()), [&](Attr *Attribute) { return isa(Attribute); }); auto Loc = getLoc(cast(*Annotate)->getAnnotation()); if (not Loc or Loc->empty()) { revng_log(Log, "Return value should have attribute annotate with reg or " "stack"); return false; } // TODO: Handle stack location. if (*Loc == "stack") { revng_log(Log, "We don't support Return value on stack for now"); return false; } LocationID = model::Register::fromCSVName(*Loc, Model->Architecture()); if (*LocationID == model::Register::Invalid) { revng_log(Log, "Unsupported register location"); return false; } } std::optional Size = 0; const QualType &FieldType = Field->getType(); auto TheFieldType = getModelTypeForClangType(FieldType); if (not TheFieldType) { revng_log(Log, "Unsupported type for a struct field"); return false; } if (AnalysisOption == ImportFromCOption::EditFunctionPrototype) { revng_assert(LocationID); ReturnValues.push_back({ *LocationID, { TheFieldType->UnqualifiedType(), TheFieldType->Qualifiers() } }); } if (FieldType->isPointerType()) { auto PointerSize = getPointerSize(Model->Architecture()); Size = PointerSize; } else if (FieldType->isArrayType()) { uint64_t NumberOfElements = 0; if (const auto *CAT = dyn_cast(FieldType)) { NumberOfElements = CAT->getSize().getZExtValue(); } else { revng_log(Log, "Unsupported array type"); return false; } Size = *(TheFieldType->UnqualifiedType().get()->size()) * NumberOfElements; } else { Size = *(TheFieldType->UnqualifiedType().get()->size()); } // Do not create fields for padding fields if (not Field->getName().starts_with(StructPaddingPrefix)) { auto &FieldModelType = Struct->Fields()[CurrentOffset]; setCustomName(FieldModelType, Field->getName()); FieldModelType.Type() = *TheFieldType; } revng_assert(Size); CurrentOffset += *Size; } // TODO: Can this be calculated/fetched automatically? Struct->Size() = CurrentOffset; switch (AnalysisOption) { case ImportFromCOption::EditType: // Remove old and add new type with the same ID. llvm::erase_if(Model->Types(), [&](UpcastablePointer &P) { return P.get()->ID() == (*Type)->ID(); }); Model->Types().insert(std::move(NewType)); break; case ImportFromCOption::EditFunctionPrototype: MultiRegisterReturnValue = ReturnValues; break; case ImportFromCOption::AddType: Model->recordNewType(std::move(NewType)); break; } return true; } bool DeclVisitor::handleUnionType(const clang::RecordDecl *RD) { revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); const RecordDecl *Definition = RD->getDefinition(); auto NewType = makeType(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = (*Type)->ID(); setCustomName(*NewType, RD->getName().str()); auto Union = cast(NewType.get()); uint64_t CurrentIndex = 0; for (const FieldDecl *Field : Definition->fields()) { if (Field->isInvalidDecl()) { revng_log(Log, "Invalid declaration for a union field"); return false; } std::vector Qualifiers; const QualType &FieldType = Field->getType(); auto TheFieldType = getModelTypeForClangType(FieldType); if (not TheFieldType) { revng_log(Log, "Unsupported type for an union field"); return false; } auto &FieldModelType = Union->Fields()[CurrentIndex]; setCustomName(FieldModelType, Field->getName()); FieldModelType.Type() = *TheFieldType; ++CurrentIndex; } if (AnalysisOption == ImportFromCOption::EditType) { // Remove old and add new type with the same ID. llvm::erase_if(Model->Types(), [&](UpcastablePointer &P) { return P.get()->ID() == (*Type)->ID(); }); Model->Types().insert(std::move(NewType)); } else { Model->recordNewType(std::move(NewType)); } return true; } bool DeclVisitor::VisitRecordDecl(const clang::RecordDecl *RD) { if (not comesFromInternalFile(RD)) return true; if (AnalysisOption != ImportFromCOption::EditFunctionPrototype and not RD->hasAttr()) { revng_log(Log, "Unions and Structs should have attribute packed"); return false; } QualType TheType = Context.getTypeDeclType(RD); if (TheType->isStructureType()) { return handleStructType(RD); } else if (TheType->isUnionType()) { return handleUnionType(RD); } else { revng_log(Log, "Unhandled record type declaration"); return false; } return true; } bool DeclVisitor::VisitEnumDecl(const EnumDecl *D) { if (not comesFromInternalFile(D)) return true; revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); if (not D->hasAttr()) { revng_log(Log, "Enums should have attribute packed"); return false; } // Parse annotate attribute used for specifying underlying type. std::optional UnderlyingType; if (D->hasAttr()) { auto Annotate = std::find_if(begin(D->getAttrs()), end(D->getAttrs()), [&](Attr *Attribute) { return isa(Attribute); }); llvm::StringRef Annotation = cast(*Annotate)->getAnnotation(); UnderlyingType = parseEnumUnderlyingType(Annotation); if (not UnderlyingType or UnderlyingType->empty()) { revng_log(Log, "Unable to parse `enum_underlying_type:` from the annotate " "attribute"); return false; } } revng_assert(UnderlyingType.has_value()); auto TheUnderlyingModelType = getEnumUnderlyingType(*UnderlyingType); if (not TheUnderlyingModelType) { revng_log(Log, "UnderlyingType of a EnumType can only be Signed or Unsigned"); return false; } auto NewType = makeType(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = (*Type)->ID(); auto *Definition = D->getDefinition(); auto TypeEnum = cast(NewType.get()); model::QualifiedType TheUnderlyingType(*TheUnderlyingModelType, {}); TypeEnum->UnderlyingType() = TheUnderlyingType; setCustomName(*TypeEnum, Definition->getName()); for (const auto *Enum : Definition->enumerators()) { auto &EnumEntry = TypeEnum->Entries()[Enum->getInitVal().getExtValue()]; std::string NewName = Enum->getName().str(); if (TypeEnum->entryName(EnumEntry) != NewName) EnumEntry.CustomName() = NewName; } if (AnalysisOption == ImportFromCOption::EditType) { // Remove old and add new type with the same ID. llvm::erase_if(Model->Types(), [&](UpcastablePointer &P) { return P.get()->ID() == (*Type)->ID(); }); Model->Types().insert(std::move(NewType)); } else { Model->recordNewType(std::move(NewType)); } return true; } void DeclVisitor::run(clang::TranslationUnitDecl *TUD) { this->TraverseDecl(TUD); } bool DeclVisitor::TraverseDecl(clang::Decl *D) { // This can happen due to an error in the code. if (!D) return true; setupLineAndColumn(D); if (isa(D)) VisitEnumDecl(cast(D)); clang::RecursiveASTVisitor::TraverseDecl(D); return true; } void HeaderToModel::HandleTranslationUnit(ASTContext &Context) { Model->Architecture() = Model->Architecture(); Model->DefaultABI() = Model->DefaultABI(); clang::TranslationUnitDecl *TUD = Context.getTranslationUnitDecl(); DeclVisitor(Model, Context, Type, Function, Error, AnalysisOption).run(TUD); } std::unique_ptr HeaderToModelEditTypeAction::newASTConsumer() { std::optional FunctionToBeEdited{ std::nullopt }; return std::make_unique(Model, Type, FunctionToBeEdited, Error, AnalysisOption); } std::unique_ptr HeaderToModelEditFunctionAction::newASTConsumer() { std::optional TypeToBeEdited{ std::nullopt }; return std::make_unique(Model, TypeToBeEdited, Function, Error, AnalysisOption); } std::unique_ptr HeaderToModelAddTypeAction::newASTConsumer() { std::optional TypeToBeEdited{ std::nullopt }; std::optional FunctionToBeEdited{ std::nullopt }; return std::make_unique(Model, TypeToBeEdited, FunctionToBeEdited, Error, AnalysisOption); } std::unique_ptr HeaderToModelAction::CreateASTConsumer(CompilerInstance &, llvm::StringRef) { return newASTConsumer(); } bool HeaderToModelAction::BeginInvocation(clang::CompilerInstance &CI) { DiagConsumer = new HeaderToModelDiagnosticConsumer(CI.getDiagnostics()); CI.getDiagnostics().setClient(DiagConsumer, /*ShouldOwnClient=*/true); return true; } void HeaderToModelAction::EndSourceFile() { if (DiagConsumer->getError()) { Error = DiagConsumer->getError(); } } void HeaderToModelDiagnosticConsumer::EndSourceFile() { Client->EndSourceFile(); } using Level = DiagnosticsEngine::Level; void HeaderToModelDiagnosticConsumer::HandleDiagnostic(Level DiagLevel, const Diagnostic &Info) { SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); llvm::raw_svector_ostream DiagMessageStream(OutStr); std::string Text; std::string ErrorLocation; llvm::raw_string_ostream OS(Text); auto *DiagOpts = &Info.getDiags()->getDiagnosticOptions(); uint64_t StartOfLocationInfo = OS.tell(); TextDiagnostic::printDiagnosticLevel(OS, DiagLevel, DiagOpts->ShowColors); const bool IsSupplemental = DiagLevel == DiagnosticsEngine::Note; TextDiagnostic::printDiagnosticMessage(OS, IsSupplemental, DiagMessageStream.str(), OS.tell() - StartOfLocationInfo, DiagOpts->MessageLength, DiagOpts->ShowColors); unsigned Line = 0; unsigned Column = 0; std::string FileName; if (Info.getLocation().isValid()) { FullSourceLoc Location(Info.getLocation(), Info.getSourceManager()); Line = Location.getLineNumber(); Column = Location.getColumnNumber(); FileName = Location.getPresumedLoc().getFilename(); } ErrorLocation = FileName + ":" + std::to_string(Line) + ":" + std::to_string(Column) + ": "; Text = ErrorLocation + Text; // Report all the messages coming from clang. if (Error) Text = Error->ErrorMessage + Text; Error = { Text, Line, Column }; OS.flush(); } } // end namespace tooling } // end namespace clang