// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "clang/AST/Decl.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnostic.h" #include "revng/ABI/ModelHelpers.h" #include "revng/Model/Processing.h" #include "revng/PTML/CAttributes.h" #include "revng/PTML/CBuilder.h" #include "revng/Pipes/Ranks.h" #include "revng/Support/Debug.h" #include "revng/TypeNames/ModelCBuilder.h" #include "HeaderToModel.h" using namespace model; using namespace revng; static constexpr llvm::StringRef InputCFile = "revng-input.c"; static constexpr llvm::StringRef PrimitiveTypeHeader = "primitive-types.h"; static constexpr llvm::StringRef RawABIPrefix = "raw_"; namespace clang { namespace tooling { class HeaderToModel : public ASTConsumer { public: HeaderToModel(TupleTree &Model, std::optional Type, MetaAddress FunctionEntry, ImportingErrorList &Errors, enum ImportFromCOption AnalysisOption) : Model(Model), Type(Type), FunctionEntry(FunctionEntry), Errors(Errors), AnalysisOption(AnalysisOption) { // Either one of these two should be null, since the editing features are // exclusive. revng_assert(not Type or not FunctionEntry.isValid()); } virtual void HandleTranslationUnit(ASTContext &Context) override; private: TupleTree &Model; std::optional Type; MetaAddress FunctionEntry; ImportingErrorList &Errors; enum ImportFromCOption AnalysisOption; }; class DeclVisitor : public clang::RecursiveASTVisitor { private: TupleTree &Model; ASTContext &Context; std::optional Type; MetaAddress FunctionEntry; ImportingErrorList &Errors; 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 model::Type. using RawLocation = std::pair; std::optional> MultiRegisterReturnValue; public: DeclVisitor(TupleTree &Model, ASTContext &Context, std::optional Type, MetaAddress FunctionEntry, ImportingErrorList &Errors, 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, llvm::StringRef 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 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. model::UpcastableType makePrimitive(const BuiltinType *UnderlyingBuiltin, QualType Type); // Get model type for clang::RecordType (Struct/Unoion). model::UpcastableType getTypeForRecordType(const clang::RecordType *RecordType, const QualType &ClangType); // Get model type for clang::EnumType. model::UpcastableType getTypeForEnumType(const clang::EnumType *EnumType); template T> model::UpcastableType makeTypeByNameOrID(llvm::StringRef Name); RecursiveCoroutine getModelTypeForClangType(const QualType &QT); template std::optional parseStringAnnotation(const Type &Declaration, ImportingErrorList &Errors); template std::optional parseIntegerAnnotation(const Type &Declaration, ImportingErrorList &Errors); }; DeclVisitor::DeclVisitor(TupleTree &Model, ASTContext &Context, std::optional Type, MetaAddress FunctionEntry, ImportingErrorList &Errors, enum ImportFromCOption AnalysisOption) : Model(Model), Context(Context), Type(Type), FunctionEntry(FunctionEntry), Errors(Errors), AnalysisOption(AnalysisOption) { } template std::optional DeclVisitor::parseStringAnnotation(const Type &Declaration, ImportingErrorList &Errors) { static constexpr auto Prefix = ptml::AttributeRegistry::getPrefix(); std::optional Result; if (Declaration.template hasAttr()) { for (auto &Attribute : Declaration.getAttrs()) { if (auto *Cast = llvm::dyn_cast(Attribute)) { llvm::StringRef Annotation = Cast->getAnnotation(); if (not Annotation.startswith(Prefix)) continue; llvm::StringRef Value = Annotation.substr(Prefix.size()); if (Result.has_value() && Result.value() != Value) { std::string ErrorPrefix = "import-from-c:"; SourceManager &SM = Context.getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(Attribute->getRange().getBegin()); if (Loc.isValid()) ErrorPrefix += std::to_string(Loc.getLine()) + ":" + std::to_string(Loc.getColumn()) + ":"; Errors.emplace_back(ErrorPrefix + " Multiple conflicting values (`" + Result.value().str() + "` and `" + Value.str() + "`) were found for the `" + std::string(Macro) + "` annotation.\n"); return std::nullopt; } Result = Value; } } } return Result; } template std::optional DeclVisitor::parseIntegerAnnotation(const Type &Declaration, ImportingErrorList &Errors) { std::optional Result = parseStringAnnotation(Declaration, Errors); if (not Result.has_value()) return std::nullopt; uint64_t IntegerResult; if (Result->getAsInteger(0, IntegerResult)) { Errors.emplace_back("import-from-c: Ignoring a non-integer value (`" + Result->str() + "`) of an integer annotation: `" + std::string(Macro) + "`.\n"); return std::nullopt; } return IntegerResult; } static model::Architecture::Values getRawABIArchitecture(llvm::StringRef ABI) { revng_assert(ABI.starts_with(RawABIPrefix)); return model::Architecture::fromName(ABI.substr(RawABIPrefix.size())); } model::UpcastableType DeclVisitor::makePrimitive(const BuiltinType *UnderlyingBuiltin, QualType Type) { revng_assert(UnderlyingBuiltin); auto AsElaboratedType = Type->getAs(); if (not AsElaboratedType) { PrintingPolicy Policy(Context.getLangOpts()); Errors.emplace_back("import-from-c: Builtin type `" + UnderlyingBuiltin->getName(Policy).str() + "` not allowed, please use a revng " "model::PrimitiveType instead.\n"); return model::UpcastableType::empty(); } while (auto Typedef = AsElaboratedType->getAs()) { auto TheUnderlyingType = Typedef->getDecl()->getUnderlyingType(); if (not TheUnderlyingType->getAs()) break; AsElaboratedType = TheUnderlyingType->getAs(); } std::string TypeName = AsElaboratedType->getNamedType().getAsString(); if (model::PrimitiveType::fromCName(TypeName).isEmpty()) { Errors.emplace_back("import-from-c: `" + AsElaboratedType->getNamedType().getAsString() + "` type is not supported, please use a revng " "model::PrimitiveType instead.\n"); return model::UpcastableType::empty(); } switch (UnderlyingBuiltin->getKind()) { case BuiltinType::UInt128: return model::PrimitiveType::makeUnsigned(16); case BuiltinType::Int128: return model::PrimitiveType::makeSigned(16); case BuiltinType::ULongLong: case BuiltinType::ULong: return model::PrimitiveType::makeUnsigned(8); case BuiltinType::LongLong: case BuiltinType::Long: return model::PrimitiveType::makeSigned(8); case BuiltinType::WChar_U: case BuiltinType::UInt: return model::PrimitiveType::makeUnsigned(4); case BuiltinType::WChar_S: case BuiltinType::Char32: case BuiltinType::Int: return model::PrimitiveType::makeSigned(4); case BuiltinType::UShort: return model::PrimitiveType::makeUnsigned(2); case BuiltinType::Char16: case BuiltinType::Short: return model::PrimitiveType::makeSigned(2); case BuiltinType::Char_S: case BuiltinType::SChar: case BuiltinType::Char8: case BuiltinType::Bool: return model::PrimitiveType::makeUnsigned(1); case BuiltinType::Char_U: case BuiltinType::UChar: return model::PrimitiveType::makeSigned(1); case BuiltinType::Void: return model::PrimitiveType::makeVoid(); case BuiltinType::Float16: return model::PrimitiveType::makeFloat(2); case BuiltinType::Float: return model::PrimitiveType::makeFloat(4); case BuiltinType::Double: return model::PrimitiveType::makeFloat(8); case BuiltinType::Float128: case BuiltinType::LongDouble: return model::PrimitiveType::makeFloat(16); default: Errors.emplace_back("import-from-c: Unable to handle a primitive type.\n"); } return model::UpcastableType::empty(); } static bool onlyContainsNumbers(llvm::StringRef Name) { for (char Character : Name) if (not std::isdigit(Character)) return false; return true; } template T> model::UpcastableType DeclVisitor::makeTypeByNameOrID(llvm::StringRef Name) { // Try to find by name first. for (auto &Type : Model->TypeDefinitions()) if (llvm::isa(Type.get())) if (Type->Name() == Name) return Model->makeType(Type->key()); // Getting here means we didn't manage to find it, // let's try parsing the name. size_t Tail = Name.rfind("_"); if (Tail != std::string::npos && onlyContainsNumbers(Name.substr(Tail + 1))) { std::string ID = std::string(Name.substr(Tail + 1)); llvm::Expected MaybeID = fromString(ID); if (MaybeID) { return Model->makeType(model::TypeDefinition::Key{ *MaybeID, T::AssociatedKind }); } else { Errors.emplace_back(consumeToString(MaybeID)); } } return model::UpcastableType::empty(); } model::UpcastableType 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) { Errors.emplace_back("import-from-c: There should be a typedef for struct " "that defines the primitive type.\n"); return model::UpcastableType::empty(); } auto TypeName = AsTypedef->getDecl()->getName(); auto R = model::PrimitiveType::fromCName(TypeName); revng_assert(R); return R; } auto Name = RecordType->getDecl()->getName(); if (Name.empty()) { Errors.emplace_back("import-from-c: Nameless structs and unions are not " "supported here, since we have no way to trace them " "back to one of the types present in the model.\n"); return model::UpcastableType::empty(); } if (RecordType->isStructureType()) { if (auto Struct = makeTypeByNameOrID(Name)) return Struct; } else if (RecordType->isUnionType()) { if (auto Union = makeTypeByNameOrID(Name)) return Union; } Errors.emplace_back("import-from-c: Unknown struct or union: `" + Name.str() + "`.\n"); return model::UpcastableType::empty(); } model::UpcastableType DeclVisitor::getTypeForEnumType(const clang::EnumType *EnumType) { revng_assert(EnumType); revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); auto EnumName = EnumType->getDecl()->getName(); if (EnumName.empty()) { Errors.emplace_back("import-from-c: Nameless enums are not supported here, " "since we have no way to trace them back to one of the " "types present in the model.\n"); return model::UpcastableType::empty(); } if (auto Enum = makeTypeByNameOrID(EnumName)) return Enum; Errors.emplace_back("import-from-c: Unknown enum: `" + EnumName.str() + "`.\n"); return model::UpcastableType::empty(); } bool DeclVisitor::comesFromInternalFile(const clang::Decl *D) { SourceManager &SM = Context.getSourceManager(); PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); if (!Loc.isValid()) 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()) 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()) return; CurrentLineNumber = Loc.getLine(); CurrentColumnNumber = Loc.getColumn(); } RecursiveCoroutine DeclVisitor::getModelTypeForClangType(const QualType &QT) { model::UpcastableType R; if (const BuiltinType *AsBuiltinType = QT->getAs()) { R = makePrimitive(AsBuiltinType, QT); } else if (const PointerType *Pointer = QT->getAs()) { QualType Pointee = Pointer->getPointeeType(); R = model::PointerType::make(rc_recur getModelTypeForClangType(Pointee), Model->Architecture()); } else if (QT->isArrayType()) { if (const auto *CAT = dyn_cast(QT)) { QualType ElementType = Context.getBaseElementType(QT); uint64_t NumberOfElements = CAT->getSize().getZExtValue(); R = model::ArrayType::make(rc_recur getModelTypeForClangType(ElementType), NumberOfElements); } else { // Here we can face `clang::VariableArrayType` and // `clang::IncompleteArrayType`. Errors.emplace_back("import-from-c: Unsupported type used as an " "array.\n"); } } else if (const RecordType *AsRecordType = QT->getAs()) { R = getTypeForRecordType(AsRecordType, QT); } else if (const EnumType *AsEnum = QT->getAs()) { R = getTypeForEnumType(AsEnum); } else if (const auto *AsFn = QT->getAs()) { if (const TypedefType *AsTypedef = QT->getAs()) { auto Name = AsTypedef->getDecl()->getName(); if (auto CFT = makeTypeByNameOrID(Name)) R = std::move(CFT); else if (auto Rw = makeTypeByNameOrID(Name)) R = std::move(Rw); else Errors.emplace_back("import-from-c: Unknown typedef: `" + Name.str() + "`.\n"); } else { Errors.emplace_back("import-from-c: Model has to contain a typedef for " "the function prototype.\n"); } } else { Errors.emplace_back("import-from-c: The type cannot be represented in the " "model.\n"); } if (not R.isEmpty() and QT.isConstQualified()) R->IsConst() = true; rc_return R; } bool DeclVisitor::VisitFunctionDecl(const clang::FunctionDecl *FD) { if (not comesFromInternalFile(FD)) return true; revng_assert(FD); revng_assert(AnalysisOption == ImportFromCOption::EditFunctionPrototype); std::optional ABI = parseStringAnnotation<"_ABI">(*FD, Errors); if (not ABI.has_value() or ABI->empty()) { Errors.emplace_back("import-from-c failed: Functions without an " "`_ABI($name)` or `_ABI(raw_$arch)` annotation are not " "allowed.\n"); return false; } bool IsRawFunctionType = ABI->starts_with(RawABIPrefix); auto NewType = IsRawFunctionType ? makeTypeDefinition() : makeTypeDefinition(); if (not IsRawFunctionType) { auto TheModelABI = model::ABI::fromName(*ABI); if (TheModelABI == model::ABI::Invalid) { Errors.emplace_back("import-from-c failed: Unknown ABI: `" + ABI->str() + "`.\n"); return false; } auto &FunctionType = llvm::cast(*NewType); FunctionType.ABI() = TheModelABI; auto TheRetClangType = FD->getReturnType(); model::UpcastableType RetType = getModelTypeForClangType(TheRetClangType); if (not RetType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "the return value.\n"); return false; } FunctionType.ReturnType() = std::move(RetType); // Handle params. uint32_t Index = 0; for (unsigned I = 0, N = FD->getNumParams(); I != N; ++I) { auto QT = FD->getParamDecl(I)->getType(); model::UpcastableType ParamType = getModelTypeForClangType(QT); if (not ParamType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "the argument #" + std::to_string(I) + ".\n"); return false; } model::Argument &NewArgument = FunctionType.Arguments()[Index]; // TODO: we shouldn't write generated names into the model. NewArgument.Name() = FD->getParamDecl(I)->getName(); NewArgument.Type() = std::move(ParamType); ++Index; } } else { auto TheRetClangType = FD->getReturnType(); auto &TheRawFunctionType = llvm::cast(*NewType); auto Architecture = getRawABIArchitecture(*ABI); if (Architecture == model::Architecture::Invalid) { Errors.emplace_back("import-from-c failed: Unknown architecture: `" + ABI->substr(RawABIPrefix.size()).str() + "`.\n"); 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) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "the return value.\n"); return false; } for (auto &[Location, Type] : *MultiRegisterReturnValue) { model::NamedTypedRegister &NTR = ReturnValuesInserter.emplace(Location); NTR.Type() = Type; } } else { std::optional Register = parseStringAnnotation<"_REG">(*FD, Errors); if (not Register.has_value()) { std::optional Stack = parseStringAnnotation<"_STACK">(*FD, Errors); if (Stack.has_value()) { Errors.emplace_back("import-from-c failed: Only register values are " "allowed as a part of a raw function's return " "value. As such, they must not use _STACK " "annotation.\n"); return false; } else { Errors.emplace_back("import-from-c failed: Return values of a raw " "function must have a _REG($name) annotation.\n"); return false; } } model::UpcastableType RetType = getModelTypeForClangType(TheRetClangType); if (not RetType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "the return value.\n"); return false; } auto Location = model::Register::fromRegisterName(*Register, Model->Architecture()); if (Location == model::Register::Invalid) { Errors.emplace_back("import-from-c: While parsing the return value:\n"); Errors.emplace_back("import-from-c failed: Unknown register: `" + Register->str() + "`.\n"); return false; } auto &ReturnValueReg = ReturnValuesInserter.emplace(Location); ReturnValueReg.Type() = std::move(RetType); } auto ArgumentsInserter = TheRawFunctionType.Arguments().batch_insert(); for (unsigned I = 0, N = FD->getNumParams(); I != N; ++I) { auto ParamDecl = FD->getParamDecl(I); auto QT = ParamDecl->getType(); model::UpcastableType ParamType = getModelTypeForClangType(QT); if (not ParamType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "the argument #" + std::to_string(I) + ".\n"); return false; } std::optional Register = parseStringAnnotation<"_REG">(*ParamDecl, Errors); std::optional Stack = parseStringAnnotation<"_STACK">(*ParamDecl, Errors); if (not Register.has_value()) { if (not Stack.has_value()) { Errors.emplace_back("import-from-c failed: Argument #" + std::to_string(I) + " is missing it's location annotation.\n"); Errors.emplace_back(" Please add either " "`_REG($name)` or `_STACK`.\n"); return false; } else { if (I != N - 1) { Errors.emplace_back("import-from-c failed: Only the very last RFT " "argument is allowed to represent stack, which " "also means there can only be one.\n"); Errors.emplace_back(" Please either remove " "`_STACK` annotation from the argument #" + std::to_string(I) + " or move it into the stack argument " "struct.\n"); return false; } if (not ParamType->isStruct()) { Errors.emplace_back("import-from-c failed: RFT stack argument must " "be a " "struct. You can use fields of such a struct " "to represent separate arguments.\n"); return false; } revng_assert(TheRawFunctionType.StackArgumentsType().isEmpty()); TheRawFunctionType.StackArgumentsType() = std::move(ParamType); if (ParamDecl->getName() != "stack") { Errors.emplace_back("import-from-c: stack argument name (`" + ParamDecl->getName().str() + "`) was ignored, as model stores the struct " "as is.\n"); } } } else { if (Stack.has_value()) { Errors.emplace_back("import-from-c failed: A single argument cannot " "use both a register and stack: the model does " "not support that. Please use two separate " "arguments.\n"); return false; } using namespace model; auto Location = Register::fromRegisterName(*Register, Model->Architecture()); if (Location == Register::Invalid) { Errors.emplace_back("import-from-c: While parsing argument #" + std::to_string(I) + ":\n"); Errors.emplace_back("import-from-c failed: Unknown register: `" + Register->str() + "`.\n"); return false; } NamedTypedRegister &ParamReg = ArgumentsInserter.emplace(Location); ParamReg.Type() = std::move(ParamType); if (not ParamDecl->getName().empty()) ParamReg.Name() = ParamDecl->getName(); } } } // Update the name in case it changed. auto &ModelFunction = Model->Functions()[FunctionEntry]; // TODO: we shouldn't write generated names into the model. ModelFunction.Name() = FD->getName(); // 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 RawFunctionDefinition 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 ABI = parseStringAnnotation<"_ABI">(*D, Errors); if (not ABI.has_value()) { Errors.emplace_back("import-from-c failed: a function typedef must " "either have `_ABI($name)` or `_ABI(raw_$arch)` " "annotation attached.\n"); return false; } if (ABI->empty()) { Errors.emplace_back("import-from-c failed: _ABI annotation must not be " "empty.\n"); Errors.emplace_back(" Please specify an abi name or " "an architecture name.\n"); return false; } return VisitFunctionPrototype(Fn, *ABI); } // Regular, non-function, typedef. model::UpcastableType ModelTypedefType = getModelTypeForClangType(TheType); if (not ModelTypedefType) { Errors.emplace_back("import-from-c failed: Unable to parse the underlying " "type of the typedef.\n"); return false; } auto &&[ID, Kind] = *Type; auto NewTypedef = model::makeTypeDefinition(); if (AnalysisOption == ImportFromCOption::EditType) NewTypedef->ID() = ID; auto TheTypeTypeDef = cast(NewTypedef.get()); TheTypeTypeDef->UnderlyingType() = std::move(ModelTypedefType); // TODO: we shouldn't write generated names into the model. TheTypeTypeDef->Name() = D->getName(); if (AnalysisOption == ImportFromCOption::EditType) { revng_assert(*Type == NewTypedef->key()); Model->TypeDefinitions().erase(*Type); Model->TypeDefinitions().insert(std::move(NewTypedef)); } else { Model->recordNewType(std::move(NewTypedef)); } return true; } bool DeclVisitor::VisitFunctionPrototype(const FunctionProtoType *FP, llvm::StringRef ABI) { revng_assert(AnalysisOption != ImportFromCOption::EditFunctionPrototype); revng_assert(ABI != ""); bool IsRawFunctionType = ABI.starts_with(RawABIPrefix); auto NewType = IsRawFunctionType ? makeTypeDefinition() : makeTypeDefinition(); auto &&[ID, Kind] = *Type; if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = ID; if (not IsRawFunctionType) { auto &FunctionType = llvm::cast(*NewType); auto TheModelABI = model::ABI::fromName(ABI); if (TheModelABI == model::ABI::Invalid) { Errors.emplace_back("import-from-c failed: Unknown ABI: `" + ABI.str() + "`.\n"); return false; } FunctionType.ABI() = TheModelABI; auto TheRetClangType = FP->getReturnType(); model::UpcastableType RetType = getModelTypeForClangType(TheRetClangType); if (not RetType) { Errors.emplace_back("import-from-c failed: Unable to parse the return " "value type.\n"); return false; } FunctionType.ReturnType() = std::move(RetType); // Handle params. uint32_t Index = 0; for (auto QT : FP->getParamTypes()) { model::UpcastableType ParamType = getModelTypeForClangType(QT); if (not ParamType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "argument #`" + std::to_string(Index) + "`.\n"); return false; } model::Argument &NewArgument = FunctionType.Arguments()[Index]; NewArgument.Type() = std::move(ParamType); ++Index; } } else { auto Architecture = getRawABIArchitecture(ABI); if (Architecture == model::Architecture::Invalid) { Errors.emplace_back("import-from-c failed: Unknown architecture: `" + ABI.substr(RawABIPrefix.size()).str() + "`.\n"); return false; } // TODO: Since we do not have info about parameters annotation, we use // default raw function. auto Default = cast(*Model->defaultPrototype()); auto &FunctionType = llvm::cast(*NewType); FunctionType.Architecture() = Architecture; FunctionType.Arguments() = Default.Arguments(); FunctionType.ReturnValues() = Default.ReturnValues(); FunctionType.PreservedRegisters() = Default.PreservedRegisters(); FunctionType.FinalStackOffset() = Default.FinalStackOffset(); } if (AnalysisOption == ImportFromCOption::EditType) { revng_assert(*Type == NewType->key()); Model->TypeDefinitions().erase(*Type); Model->TypeDefinitions().insert(std::move(NewType)); } else { Model->recordNewType(std::move(NewType)); } return true; } bool DeclVisitor::handleStructType(const clang::RecordDecl *RD) { const RecordDecl *Definition = RD->getDefinition(); if (Definition == nullptr) { Errors.emplace_back("import-from-c failed: Unable to parse the struct.\n"); return false; } auto &&[ID, Kind] = *Type; auto NewType = makeTypeDefinition(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = ID; // TODO: we shouldn't write generated names into the model. NewType->Name() = RD->getName(); auto *Struct = cast(NewType.get()); uint64_t CurrentOffset = 0; // // Iterate over the struct fields // llvm::SmallVector ReturnValues; for (const FieldDecl *Field : Definition->fields()) { if (Field->isInvalidDecl()) { Errors.emplace_back("import-from-c failed: The declaration of the struct " "field #`" + std::to_string(Struct->Fields().size()) + "` is not valid.\n"); return false; } model::Register::Values Location; if (AnalysisOption == ImportFromCOption::EditFunctionPrototype) { std::optional Stack = parseStringAnnotation<"_STACK">(*Field, Errors); if (Stack.has_value()) { Errors.emplace_back("import-from-c failed: Only register values are " "allowed as a part of a raw function's return " "value. As such, they must not use _STACK " "annotation.\n"); return false; } std::optional Register = parseStringAnnotation<"_REG">(*Field, Errors); if (not Register.has_value()) { Errors.emplace_back("import-from-c failed: Return values of a raw " "function must have a _REG($name) annotation.\n"); return false; } Location = model::Register::fromRegisterName(*Register, Model->Architecture()); if (Location == model::Register::Invalid) { Errors.emplace_back("import-from-c: While parsing return value #" + std::to_string(Struct->Fields().size()) + ":\n"); Errors.emplace_back("import-from-c failed: Unknown register: `" + Register->str() + "`.\n"); return false; } } std::optional Size = 0; const QualType &ClangFieldType = Field->getType(); model::UpcastableType ModelField = getModelTypeForClangType(ClangFieldType); if (ModelField.isEmpty()) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "struct field #" + std::to_string(Struct->Fields().size()) + ".\n"); return false; } if (AnalysisOption == ImportFromCOption::EditFunctionPrototype) ReturnValues.emplace_back(Location, ModelField); if (ClangFieldType->isPointerType()) { Size = model::Architecture::getPointerSize(Model->Architecture()); } else if (ClangFieldType->isArrayType()) { uint64_t NumberOfElements = 0; if (const auto *CAT = dyn_cast(ClangFieldType)) { NumberOfElements = CAT->getSize().getZExtValue(); } else { Errors.emplace_back("import-from-c failed: Unsupported array type.\n"); return false; } const model::Type &Element = *ModelField->toArray().ElementType(); Size = *Element.size() * NumberOfElements; } else { Size = *ModelField->size(); } const auto &Config = Model->Configuration().Naming(); bool IsPadding = Field->getName().starts_with(Config.StructPaddingPrefix()); auto ExplicitOffset = parseIntegerAnnotation<"_STARTS_AT">(*Field, Errors); if (ExplicitOffset.has_value()) { if (IsPadding) { Errors.emplace_back("import-from-c: While parsing field #" + std::to_string(Struct->Fields().size()) + ":\n"); Errors.emplace_back("import-from-c failed: Padding fields (`uint8_t " "padding_at_$offset[$size]`) must not have " "`_STARTS_AT` annotation attached.\n"); return false; } if (not Struct->Fields().empty() and CurrentOffset > *ExplicitOffset) { Errors.emplace_back("import-from-c: While parsing field #" + std::to_string(Struct->Fields().size()) + ":\n"); Errors.emplace_back("import-from-c failed: `_STARTS_AT` must not be " "used to make fields overlap.\n"); return false; } CurrentOffset = *ExplicitOffset; } if (not IsPadding) { auto &FieldModelType = Struct->Fields()[CurrentOffset]; // TODO: we shouldn't write generated names into the model. FieldModelType.Name() = Field->getName(); FieldModelType.Type() = std::move(ModelField); } else { // Do not create fields for padding } revng_assert(Size); CurrentOffset += *Size; } if (std::optional ExplicitSize = parseIntegerAnnotation<"_SIZE">(*Definition, Errors)) { // Prefer explicit size if it's available. Struct->Size() = *ExplicitSize; } else { // If not, just use final offset, Struct->Size() = CurrentOffset; // Unless we're editing a type and have access to the previous size. if (Type.has_value()) if (auto *MaybeType = Model->TypeDefinitions().tryGet(*Type)) if ((*MaybeType)->isObject()) if (auto OldSize = *(*MaybeType)->size(); Struct->Size() < OldSize) Struct->Size() = OldSize; } if (parseStringAnnotation<"_CAN_CONTAIN_CODE">(*RD, Errors)) Struct->CanContainCode() = true; switch (AnalysisOption) { case ImportFromCOption::EditType: revng_assert(*Type == NewType->key()); Model->TypeDefinitions().erase(*Type); Model->TypeDefinitions().insert(std::move(NewType)); break; case ImportFromCOption::EditFunctionPrototype: MultiRegisterReturnValue = std::move(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(); if (Definition == nullptr) { Errors.emplace_back("import-from-c failed: Unable to parse the union.\n"); return false; } auto &&[ID, Kind] = *Type; auto NewType = makeTypeDefinition(); if (AnalysisOption == ImportFromCOption::EditType) NewType->ID() = ID; // TODO: we shouldn't write generated names into the model. NewType->Name() = RD->getName(); auto Union = cast(NewType.get()); uint64_t CurrentIndex = 0; for (const FieldDecl *Field : Definition->fields()) { if (Field->isInvalidDecl()) { Errors.emplace_back("import-from-c failed: The declaration of the union " "field #`" + std::to_string(Union->Fields().size()) + "` is not valid.\n"); return false; } const QualType &FieldType = Field->getType(); model::UpcastableType TheFieldType = getModelTypeForClangType(FieldType); if (not TheFieldType) { Errors.emplace_back("import-from-c failed: Unable to parse the type of " "union field #" + std::to_string(Union->Fields().size()) + ".\n"); return false; } auto &FieldModelType = Union->Fields()[CurrentIndex]; // TODO: we shouldn't write generated names into the model. FieldModelType.Name() = Field->getName(); FieldModelType.Type() = std::move(TheFieldType); ++CurrentIndex; } if (AnalysisOption == ImportFromCOption::EditType) { revng_assert(*Type == NewType->key()); Model->TypeDefinitions().erase(*Type); Model->TypeDefinitions().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()) { Errors.emplace_back("import-from-c failed: Unions and Structs must be " "`_PACKED`.\n"); return false; } QualType TheType = Context.getTypeDeclType(RD); if (TheType->isStructureType()) { return handleStructType(RD); } else if (TheType->isUnionType()) { return handleUnionType(RD); } else { Errors.emplace_back("import-from-c failed: As of now, only struct and " "union record types are supported.\n"); Errors.emplace_back(" Please rewrite your type as one " "of those two.\n"); 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()) { Errors.emplace_back("import-from-c failed: Enums must be `_PACKED`.\n"); return false; } // Parse annotate attribute used for specifying underlying type. auto UnderlyingTypeName = parseStringAnnotation<"_ENUM_UNDERLYING">(*D, Errors); if (not UnderlyingTypeName.has_value()) { Errors.emplace_back("import-from-c failed: Enums without an " "`_ENUM_UNDERLYING($type)` annotation are not " "allowed.\n"); return false; } if (UnderlyingTypeName->empty()) { Errors.emplace_back("import-from-c failed: `_ENUM_UNDERLYING` must not be " "empty: please specify a valid type name.\n"); return false; } revng_assert(UnderlyingTypeName.has_value()); auto UnderlyingType = model::PrimitiveType::fromCName(*UnderlyingTypeName); if (not UnderlyingType) { Errors.emplace_back("import-from-c failed: unknown primitive type: `" + UnderlyingTypeName->str() + "`.\n"); return false; } else if (not UnderlyingType->isSignedPrimitive() and not UnderlyingType->isUnsignedPrimitive()) { Errors.emplace_back("import-from-c failed: Underlying type of an enum can " "only be signed or unsigned.\n"); Errors.emplace_back(" `" + UnderlyingTypeName->str() + "` was found instead.\n"); return false; } model::EnumDefinition *NewType = nullptr; if (AnalysisOption == ImportFromCOption::EditType) { revng_assert(Type != std::nullopt); model::TypeDefinition &Definition = *Model->TypeDefinitions().at(*Type); if (auto *Enum = llvm::dyn_cast(&Definition)) { NewType = Enum; } else { // It seems like the kind of the type got changed. Since it affects // the key we need to erase the old type before adding the new one. Model->TypeDefinitions().erase(*Type); NewType = &Model->makeEnumDefinition().first; } } NewType->UnderlyingType() = std::move(UnderlyingType); auto *Definition = D->getDefinition(); // TODO: we shouldn't write generated names into the model. NewType->Name() = Definition->getName(); for (const auto *Enum : Definition->enumerators()) { auto Value = Enum->getInitVal().getExtValue(); auto NewIterator = NewType->Entries().insert(Value).first; NewIterator->Name() = Enum->getName().str(); } 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); clang::RecursiveASTVisitor::TraverseDecl(D); return true; } void HeaderToModel::HandleTranslationUnit(ASTContext &Context) { clang::TranslationUnitDecl *TUD = Context.getTranslationUnitDecl(); DeclVisitor(Model, Context, Type, FunctionEntry, Errors, AnalysisOption) .run(TUD); } std::unique_ptr HeaderToModelEditTypeAction::newASTConsumer() { return std::make_unique(Model, Type, MetaAddress::invalid(), Errors, AnalysisOption); } std::unique_ptr HeaderToModelEditFunctionAction::newASTConsumer() { return std::make_unique(Model, /* Type = */ std::nullopt, FunctionEntry, Errors, AnalysisOption); } std::unique_ptr HeaderToModelAddTypeAction::newASTConsumer() { return std::make_unique(Model, /* Type = */ std::nullopt, MetaAddress::invalid(), Errors, 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() { std::vector MoreErrors = DiagConsumer->extractErrors(); if (not MoreErrors.empty()) llvm::move(MoreErrors, std::back_inserter(Errors)); } 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; 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); OS.flush(); 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(); } // Report all the messages coming from clang. Errors.emplace_back("clang:" + std::to_string(Line) + ":" + std::to_string(Column) + ": " + std::move(Text)); } } // end namespace tooling } // end namespace clang