diff --git a/CMakeLists.txt b/CMakeLists.txt index bfd0b1499..792cd0826 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,35 +292,6 @@ foreach( strip-dead-debug-info) endforeach() -# Produce well-known-models -file( - GLOB - WELL_KNOWN_BINARIES - "${CMAKE_INSTALL_PREFIX}/share/revng/test/tests/well-known-models/*revng-qa.compiled-with-debug*" -) - -add_custom_target(well-known-binaries ALL) - -foreach(WELL_KNOWN_BINARY IN LISTS WELL_KNOWN_BINARIES) - - get_filename_component(BASENAME "${WELL_KNOWN_BINARY}" NAME) - set(MODEL_PATH "share/revng/well-known-models/${BASENAME}.yml") - set(FULL_MODEL_PATH "${CMAKE_BINARY_DIR}/${MODEL_PATH}") - - add_custom_command( - OUTPUT "${FULL_MODEL_PATH}" - COMMAND "./bin/revng" analyze import-binary -o "${FULL_MODEL_PATH}" - "${WELL_KNOWN_BINARY}" - MAIN_DEPENDENCY "${WELL_KNOWN_BINARY}" - DEPENDS revng-all-binaries generate-revngModel-tuple-tree-code - generate-revngPipeline-tuple-tree-code - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") - add_custom_target("import-${BASENAME}" DEPENDS "${FULL_MODEL_PATH}") - - add_dependencies(well-known-binaries "import-${BASENAME}") - -endforeach() - # Custom command to create .clang-format file from revng-check-conventions add_custom_command( OUTPUT "${CMAKE_BINARY_DIR}/share/revng/.clang-format" diff --git a/include/revng/Model/Importer/WellKnownModels.h b/include/revng/Model/Importer/ImportPrototypesFromDatabase.h similarity index 77% rename from include/revng/Model/Importer/WellKnownModels.h rename to include/revng/Model/Importer/ImportPrototypesFromDatabase.h index cb971b652..f9a04158e 100644 --- a/include/revng/Model/Importer/WellKnownModels.h +++ b/include/revng/Model/Importer/ImportPrototypesFromDatabase.h @@ -9,9 +9,9 @@ namespace revng::pypeline::analyses { -class ImportWellKnownModelsAnalysis { +class ImportPrototypesFromDatabase { public: - static constexpr llvm::StringRef Name = "import-well-known-models"; + static constexpr llvm::StringRef Name = "import-prototypes-from-db"; llvm::Error run(Model &Model, const Request &Incoming, llvm::StringRef Configuration); diff --git a/lib/Model/Importer/CMakeLists.txt b/lib/Model/Importer/CMakeLists.txt index 7ebad3219..11f36ef04 100644 --- a/lib/Model/Importer/CMakeLists.txt +++ b/lib/Model/Importer/CMakeLists.txt @@ -2,10 +2,18 @@ # This file is distributed under the MIT License. See LICENSE.md for details. # -revng_add_analyses_library_internal(revngModelImporter WellKnownModels.cpp) +revng_add_analyses_library_internal(revngModelImporter + ImportPrototypesFromDatabase.cpp) -target_link_libraries(revngModelImporter revngModel revngModelPasses - revngPipeline ${LLVM_LIBRARIES}) +target_link_libraries( + revngModelImporter + revngModel + revngModelPasses + revngPipeline + revngPipes + revngSupport + ${LLVM_LIBRARIES} + sqlite3) add_subdirectory(Binary) add_subdirectory(DebugInfo) diff --git a/lib/Model/Importer/ImportPrototypesFromDatabase.cpp b/lib/Model/Importer/ImportPrototypesFromDatabase.cpp new file mode 100644 index 000000000..5c1f64af4 --- /dev/null +++ b/lib/Model/Importer/ImportPrototypesFromDatabase.cpp @@ -0,0 +1,470 @@ +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include +#include +#include + +#include "revng/Model/Architecture.h" +#include "revng/Model/Binary.h" +#include "revng/Model/Importer/ImportPrototypesFromDatabase.h" +#include "revng/Model/Importer/TypeCopier.h" +#include "revng/Model/Pass/DeduplicateCollidingNames.h" +#include "revng/Model/Pass/DeduplicateEquivalentTypes.h" +#include "revng/Model/Pass/FlattenPrimitiveTypedefs.h" +#include "revng/Pipeline/RegisterAnalysis.h" +#include "revng/Pipes/ModelGlobal.h" +#include "revng/Support/Debug.h" +#include "revng/Support/ResourceFinder.h" +#include "revng/Support/SQLite.h" + +static Logger Log("import-prototypes-from-db"); + +/// Turn a TypeDefinition body (flat YAML mapping) into a YAML list item +/// indented for inclusion under a top-level key. +/// +/// Input (each line at column 0): +/// ID: 123 +/// Kind: CABIFunctionDefinition +/// ... +/// +/// Output: +/// - ID: 123 +/// Kind: CABIFunctionDefinition +/// ... +static std::string indentAsListItem(llvm::StringRef Body) { + std::string Result; + llvm::raw_string_ostream Stream(Result); + + bool FirstLine = true; + while (not Body.empty()) { + auto [Line, Rest] = Body.split('\n'); + if (FirstLine) { + Stream << " - " << Line << "\n"; + FirstLine = false; + } else if (not Line.empty()) { + Stream << " " << Line << "\n"; + } + Body = Rest; + if (Body.empty() && Line.empty()) + break; + } + + return Result; +} + +struct LibraryInfo { + int64_t LibraryID; + std::string Header; +}; + +class PrototypeDatabase { + sqlite::Database Database; + +public: + PrototypeDatabase(llvm::StringRef Path) : Database(Path) {} + + /// Find a platform by its exact name. + /// Returns -1 if no platform with that name exists. + int64_t findPlatformByName(llvm::StringRef PlatformName) { + auto Statement = Database.makeStatement("SELECT PlatformID FROM Platform " + "WHERE Name = ?1"); + Statement.bind(1, PlatformName); + + int64_t PlatformID = -1; + for (auto [ID] : Statement.execute()) + PlatformID = ID; + + return PlatformID; + } + + /// Find the platform that resolves the most symbols among those compatible + /// with the given architecture and operating system. + /// Returns -1 if no matching platform is found. + int64_t electPlatform(const std::vector &SymbolNames, + llvm::StringRef ArchitectureName, + llvm::StringRef OperatingSystemName) { + std::string SymbolPlaceholders = sqlite::buildInClause(SymbolNames.size()); + int ArchitectureParamIndex = SymbolNames.size() + 1; + int OperatingSystemParamIndex = SymbolNames.size() + 2; + + std::string Query; + { + llvm::raw_string_ostream Stream(Query); + Stream << R"( + SELECT p.PlatformID, COUNT(DISTINCT s.Name) AS SymbolCount + FROM Platform p + JOIN Library l ON l.PlatformID = p.PlatformID + JOIN Symbol s ON s.LibraryID = l.LibraryID + WHERE p.Architecture = ?)" + << ArchitectureParamIndex << R"( + AND p.OperatingSystem = ?)" + << OperatingSystemParamIndex << R"( + AND s.Name IN ()" + << SymbolPlaceholders << R"() + GROUP BY p.PlatformID + ORDER BY SymbolCount DESC + LIMIT 1 + )"; + } + + auto Statement = Database.makeStatement(Query); + Statement.bind(SymbolNames); + Statement.bind(ArchitectureParamIndex, ArchitectureName); + Statement.bind(OperatingSystemParamIndex, OperatingSystemName); + + int64_t PlatformID = -1; + for (auto [ID, Count] : Statement.execute()) + PlatformID = ID; + + return PlatformID; + } + + /// Enumerate libraries on the given platform that provide at least one of + /// the requested symbols. + std::vector + enumerateLibraries(int64_t PlatformID, + const std::vector &SymbolNames) { + std::string SymbolPlaceholders = sqlite::buildInClause(SymbolNames.size()); + int PlatformParamIndex = SymbolNames.size() + 1; + + std::string Query; + { + llvm::raw_string_ostream Stream(Query); + Stream << R"( + SELECT DISTINCT l.LibraryID, l.Header + FROM Library l + JOIN Symbol s ON s.LibraryID = l.LibraryID + WHERE l.PlatformID = ?)" + << PlatformParamIndex << R"( + AND s.Name IN ()" + << SymbolPlaceholders << R"() + )"; + } + + auto Statement = Database.makeStatement(Query); + Statement.bind(SymbolNames); + Statement.bind(PlatformParamIndex, static_cast(PlatformID)); + + std::vector Libraries; + for (auto [LibraryID, HeaderText] : + Statement.execute()) + Libraries.push_back({ LibraryID, HeaderText.str() }); + + return Libraries; + } + + /// Build a model from a single library's symbols and type definitions, + /// parse it, then use a TypeCopier to import the prototypes into Binary. + /// + /// OriginalIDs in type definition bodies are scoped per-library, so each + /// library must be imported independently. + void importLibrary(const LibraryInfo &Library, + const std::vector &SymbolNames, + TupleTree &Binary) { + std::string SymbolPlaceholders = sqlite::buildInClause(SymbolNames.size()); + int LibraryParamIndex = SymbolNames.size() + 1; + + // Query type definitions for this library + std::string TypeDefinitionsQuery; + { + llvm::raw_string_ostream Stream(TypeDefinitionsQuery); + Stream << R"( + WITH RECURSIVE DependentTypeDefinitions AS ( -- + SELECT td.TypeDefinitionID, td.Body, td.OriginalID + FROM TypeDefinition td + JOIN Symbol s ON s.TypeDefinitionID = td.TypeDefinitionID + WHERE s.LibraryID = ?)" + << LibraryParamIndex << R"( + AND s.Name IN ()" + << SymbolPlaceholders << R"() + UNION + SELECT t.TypeDefinitionID, t.Body, t.OriginalID + FROM TypeDefinition t + JOIN TypeDefinitionDependencies dependency + ON dependency.SourceTypeDefinitionID = t.TypeDefinitionID + JOIN DependentTypeDefinitions dt + ON dependency.DestinationTypeDefinitionID = dt.TypeDefinitionID + ) + SELECT DISTINCT Body, OriginalID + FROM DependentTypeDefinitions + ORDER BY OriginalID + )"; + } + + auto TypeDefinitionsStatement = Database + .makeStatement(TypeDefinitionsQuery); + TypeDefinitionsStatement.bind(SymbolNames); + TypeDefinitionsStatement.bind(LibraryParamIndex, + static_cast(Library.LibraryID)); + + struct TypeInfo { + std::string Body; + int64_t OriginalID; + std::string Kind; + }; + + std::vector TypeDefinitions; + std::map OriginalIDToKind; + + for (auto [Body, OriginalID] : + TypeDefinitionsStatement.execute()) { + std::string BodyString = Body.str(); + + // Extract Kind from the YAML body + std::string Kind; + llvm::StringRef BodyReference(BodyString); + while (not BodyReference.empty()) { + auto [Line, Rest] = BodyReference.split('\n'); + if (Line.starts_with("Kind: ")) { + Kind = Line.substr(6).str(); + break; + } + BodyReference = Rest; + } + + OriginalIDToKind[OriginalID] = Kind; + TypeDefinitions.push_back({ std::move(BodyString), OriginalID, Kind }); + } + + revng_log(Log, "Found " << TypeDefinitions.size() << " type definitions"); + + // Query symbols for this library + std::string SymbolsQuery; + { + llvm::raw_string_ostream Stream(SymbolsQuery); + Stream << R"( + SELECT s.Name, COALESCE(td.OriginalID, -1) + FROM Symbol s + LEFT JOIN TypeDefinition td + ON s.TypeDefinitionID = td.TypeDefinitionID + WHERE s.LibraryID = ?)" + << LibraryParamIndex << R"( + AND s.Name IN ()" + << SymbolPlaceholders << R"() + )"; + } + + auto SymbolsStatement = Database.makeStatement(SymbolsQuery); + SymbolsStatement.bind(SymbolNames); + SymbolsStatement.bind(LibraryParamIndex, + static_cast(Library.LibraryID)); + + struct SymbolInfo { + std::string Name; + int64_t OriginalID; + bool HasType; + }; + + std::vector Symbols; + for (auto [Name, OriginalID] : + SymbolsStatement.execute()) { + bool HasType = OriginalID >= 0 and OriginalIDToKind.count(OriginalID) > 0; + Symbols.push_back({ Name.str(), OriginalID, HasType }); + } + + if (Symbols.empty()) { + revng_log(Log, "No matching symbols found"); + return; + } + + revng_log(Log, "Found " << Symbols.size() << " matching symbols"); + + // Compose the model YAML for this library + std::string ModelYAML; + llvm::raw_string_ostream YAMLStream(ModelYAML); + + YAMLStream << "---\n"; + + llvm::StringRef HeaderReference(Library.Header); + HeaderReference = HeaderReference.rtrim(); + YAMLStream << HeaderReference << "\n"; + + YAMLStream << "ImportedDynamicFunctions:\n"; + for (const auto &Symbol : Symbols) { + YAMLStream << " - Name: " << Symbol.Name << "\n"; + if (Symbol.HasType) { + auto KindIterator = OriginalIDToKind.find(Symbol.OriginalID); + if (KindIterator != OriginalIDToKind.end()) { + YAMLStream << " Prototype:\n"; + YAMLStream << " Kind: DefinedType\n"; + YAMLStream << " Definition: \"/TypeDefinitions/" + << Symbol.OriginalID << "-" << KindIterator->second + << "\"\n"; + } + } + } + + if (not TypeDefinitions.empty()) { + YAMLStream << "TypeDefinitions:\n"; + for (const auto &TypeDefinition : TypeDefinitions) + YAMLStream << indentAsListItem(TypeDefinition.Body); + } + + YAMLStream << "...\n"; + YAMLStream.flush(); + + // Parse the composed YAML into a model + auto MaybeModel = TupleTree::fromString(ModelYAML); + if (not MaybeModel) { + revng_log(Log, "Failed to parse composed model YAML"); + llvm::consumeError(MaybeModel.takeError()); + return; + } + + // Use TypeCopier to import prototypes into the destination model. + // The copier is fully created, used, and finalized within this scope. + TupleTree &ParsedModel = *MaybeModel; + TypeCopier Copier(ParsedModel, Binary); + + unsigned ImportedCount = 0; + + auto CopyPrototype = [&](auto &Function) { + if (Function.prototype() != nullptr or Function.Name().empty()) + return; + + auto Iterator = ParsedModel->ImportedDynamicFunctions() + .find(Function.Name()); + if (Iterator == ParsedModel->ImportedDynamicFunctions().end()) + return; + + if (const auto *Prototype = Iterator->prototype()) { + Function.Prototype() = Copier.copyTypeInto(*Prototype); + ++ImportedCount; + revng_log(Log, "Imported prototype for " << Function.Name()); + } + }; + + for (auto &Function : Binary->Functions()) + CopyPrototype(Function); + + for (auto &DynamicFunction : Binary->ImportedDynamicFunctions()) + CopyPrototype(DynamicFunction); + + Copier.finalize(); + + revng_log(Log, "Imported " << ImportedCount << " prototypes"); + } + + /// Main entry point: find the database, collect symbols without prototypes, + /// elect a platform, and import prototypes from each library. + static void run(TupleTree &Binary) { + revng_log(Log, "Starting prototype import from database"); + LoggerIndent Indent(Log); + + // Find the database + using revng::ResourceFinder; + auto MaybeDbPath = ResourceFinder.findFile("share/revng/prototypes.sqlite"); + if (not MaybeDbPath) { + revng_log(Log, "Database not found, skipping"); + return; + } + + revng_log(Log, "Using database: " << *MaybeDbPath); + + PrototypeDatabase Database(*MaybeDbPath); + + // Collect symbol names from functions without a prototype + std::vector SymbolNames; + + for (const auto &Function : Binary->Functions()) + if (Function.prototype() == nullptr and not Function.Name().empty()) + SymbolNames.push_back(Function.Name()); + + for (const auto &DynamicFunction : Binary->ImportedDynamicFunctions()) + if (DynamicFunction.prototype() == nullptr + and not DynamicFunction.Name().empty()) + SymbolNames.push_back(DynamicFunction.Name()); + + if (SymbolNames.empty()) { + revng_log(Log, "No functions without prototypes, skipping"); + return; + } + + revng_log(Log, + "Collected " << SymbolNames.size() + << " symbols without prototypes"); + + // Phase 1: elect the best platform. + // If PlatformName is set, try to find it by name first. + int64_t PlatformID = -1; + + if (not Binary->PlatformName().empty()) { + revng_log(Log, "Looking up platform by name: " << Binary->PlatformName()); + PlatformID = Database.findPlatformByName(Binary->PlatformName()); + } + + if (PlatformID < 0) { + using namespace model; + auto ArchitectureName = Architecture::getName(Binary->Architecture()); + auto OperatingSystem = Binary->OperatingSystem(); + auto OperatingSystemName = OperatingSystem::getName(OperatingSystem); + revng_log(Log, + "Electing platform for " << ArchitectureName << "/" + << OperatingSystemName); + PlatformID = Database.electPlatform(SymbolNames, + ArchitectureName, + OperatingSystemName); + } + + if (PlatformID < 0) { + revng_log(Log, "No matching platform found, skipping"); + return; + } + + revng_log(Log, "Selected platform ID: " << PlatformID); + + // Phase 2: enumerate libraries with matching symbols + auto Libraries = Database.enumerateLibraries(PlatformID, SymbolNames); + if (Libraries.empty()) { + revng_log(Log, "No libraries with matching symbols found"); + return; + } + + revng_log(Log, "Found " << Libraries.size() << " libraries to import"); + + // Phase 3: import each library independently + for (const auto &Library : Libraries) { + revng_log(Log, "Importing library " << Library.LibraryID); + LoggerIndent LibraryIndent(Log); + Database.importLibrary(Library, SymbolNames, Binary); + } + + model::flattenPrimitiveTypedefs(Binary); + deduplicateEquivalentTypes(Binary); + model::deduplicateCollidingNames(Binary); + + revng_log(Log, "Import complete"); + } +}; + +namespace revng::pypeline::analyses { + +llvm::Error ImportPrototypesFromDatabase::run(Model &TheModel, + const Request &Incoming, + llvm::StringRef Configuration) { + PrototypeDatabase::run(TheModel.get()); + return llvm::Error::success(); +} + +} // namespace revng::pypeline::analyses + +namespace { + +class ImportPrototypesFromDatabasePipelineAnalysis { +public: + static constexpr auto Name = "import-prototypes-from-db"; + std::vector> AcceptedKinds; + + void run(pipeline::ExecutionContext &Context) { + TupleTree + &Model = revng::getWritableModelFromContext(Context); + PrototypeDatabase::run(Model); + } +}; + +} // namespace + +static pipeline::RegisterAnalysis + RegisterImportPrototypesFromDatabase; diff --git a/lib/Model/Importer/WellKnownModels.cpp b/lib/Model/Importer/WellKnownModels.cpp deleted file mode 100644 index 23ba73009..000000000 --- a/lib/Model/Importer/WellKnownModels.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#include "revng/Model/Binary.h" -#include "revng/Model/Importer/TypeCopier.h" -#include "revng/Model/Importer/WellKnownModels.h" -#include "revng/Model/Pass/DeduplicateCollidingNames.h" -#include "revng/Model/Pass/FlattenPrimitiveTypedefs.h" -#include "revng/Pipeline/RegisterAnalysis.h" -#include "revng/Pipes/ModelGlobal.h" -#include "revng/Support/ResourceFinder.h" - -namespace revng::pipes { - -class WellKnownFunctionKey { -public: - model::Architecture::Values Architecture; - model::ABI::Values ABI; - std::string Name; - -private: - auto tie() const { return std::tie(Architecture, ABI, Name); } - -public: - bool operator<(const WellKnownFunctionKey &Other) const { - return tie() < Other.tie(); - } -}; - -class WellKnownModel { -public: - TupleTree FromModel; - TypeCopier Copier; - -public: - WellKnownModel(TupleTree &&FromModel, - TupleTree &DestinationModel) : - FromModel(FromModel), Copier(this->FromModel, DestinationModel) {} -}; - -class ImportWellKnownModelsAnalysis { -public: - static constexpr auto Name = "import-well-known-models"; - -public: - std::vector> AcceptedKinds; - -public: - void run(pipeline::ExecutionContext &Context); -}; - -} // namespace revng::pipes - -static void importWellKnownModels(TupleTree &Model) { - using namespace revng::pipes; - - std::vector> WellKnownModels; - std::map> - WellKnownFunctions; - - // Load all well-known models - for (const std::string &Path : - revng::ResourceFinder.list("share/revng/well-known-models", ".yml")) { - auto NewModel = llvm::cantFail(TupleTree::fromFile(Path)); - auto NewWKM = std::make_unique(std::move(NewModel), Model); - WellKnownModels.push_back(std::move(NewWKM)); - } - - // Create an index of well-known functions - for (auto &WellKnownModel : WellKnownModels) { - auto &Model = WellKnownModel->FromModel; - // Collect exported functions - for (model::Function &F : Model->Functions()) { - for (const std::string &ExportedName : F.ExportedNames()) { - WellKnownFunctions[{ - Model->Architecture(), Model->DefaultABI(), ExportedName }] = { - WellKnownModel.get(), &F - }; - } - } - } - - for (model::DynamicFunction &F : Model->ImportedDynamicFunctions()) { - // Compose the key - WellKnownFunctionKey Key = { Model->Architecture(), - Model->DefaultABI(), - F.Name() }; - - // See if it's a well-known function - auto It = WellKnownFunctions.find(Key); - if (It != WellKnownFunctions.end()) { - model::Function *WellKnownFunction = It->second.second; - - // Copy attributes - F.Attributes() = WellKnownFunction->Attributes(); - - // Copy prototype - if (const auto *NewPrototype = WellKnownFunction->prototype()) { - TypeCopier &Copier = It->second.first->Copier; - F.Prototype() = Copier.copyTypeInto(*NewPrototype); - } - } - } - - for (auto &WLF : WellKnownModels) - WLF->Copier.finalize(); - - model::flattenPrimitiveTypedefs(Model); - model::deduplicateCollidingNames(Model); -} - -void revng::pipes::ImportWellKnownModelsAnalysis::run(pipeline::ExecutionContext - &Context) { - TupleTree &Model = getWritableModelFromContext(Context); - importWellKnownModels(Model); -} - -static pipeline::RegisterAnalysis - E; - -namespace revng::pypeline::analyses { - -llvm::Error ImportWellKnownModelsAnalysis::run(Model &Model, - const Request &Incoming, - llvm::StringRef Configuration) { - importWellKnownModels(Model.get()); - return llvm::Error::success(); -} - -} // namespace revng::pypeline::analyses diff --git a/lib/Pipebox/Pipebox.cpp b/lib/Pipebox/Pipebox.cpp index 9f8da0a1b..41f6f6478 100644 --- a/lib/Pipebox/Pipebox.cpp +++ b/lib/Pipebox/Pipebox.cpp @@ -26,7 +26,7 @@ #include "revng/Lift/Lift.h" #include "revng/Lift/LinkSupportPipe.h" #include "revng/Model/Importer/Binary/ImportBinaryAnalysis.h" -#include "revng/Model/Importer/WellKnownModels.h" +#include "revng/Model/Importer/ImportPrototypesFromDatabase.h" #include "revng/Pipebox/LLVMPipe.h" #include "revng/Pipebox/MLIRPipe.h" #include "revng/Pipebox/MergeLLVMModules.h" @@ -138,7 +138,7 @@ static RegisterAnalysis A2; static RegisterAnalysis A3; static RegisterAnalysis A4; static RegisterAnalysis A5; -static RegisterAnalysis A6; +static RegisterAnalysis A6; static RegisterAnalysis A7; static RegisterAnalysis A8; static RegisterAnalysis A9; diff --git a/python/revng/internal/pipeline.yml b/python/revng/internal/pipeline.yml index a9e571514..6a404f669 100644 --- a/python/revng/internal/pipeline.yml +++ b/python/revng/internal/pipeline.yml @@ -65,7 +65,7 @@ analyses: - verify-diff - set-model - verify-model - - import-well-known-models + - import-prototypes-from-db - convert-functions-to-cabi - import-from-c @@ -73,7 +73,7 @@ analysis-lists: - name: initial-auto-analysis analyses: - parse-binary - - import-well-known-models + - import-prototypes-from-db - detect-abi - detect-stack-size - analyze-data-layout diff --git a/share/revng/pipelines/revng-pipelines.yml b/share/revng/pipelines/revng-pipelines.yml index ab7744024..f7fc6c33d 100644 --- a/share/revng/pipelines/revng-pipelines.yml +++ b/share/revng/pipelines/revng-pipelines.yml @@ -88,12 +88,11 @@ Branches: From debug information, all the data structures and function prototypes are imported. We currently support DWARF and CodeView (`.pdb`) debug info. - - Name: import-well-known-models - Type: import-well-known-models + - Name: import-prototypes-from-db + Type: import-prototypes-from-db UsedContainers: [] Docs: |- - Import the prototype of certain well-known functions from the C - standard library. + Import function prototypes from a database. - Name: convert-functions-to-cabi Type: convert-functions-to-cabi UsedContainers: [] @@ -725,7 +724,7 @@ AnalysesLists: - Name: revng-initial-auto-analysis Analyses: - import-binary - - import-well-known-models + - import-prototypes-from-db - detect-abi - detect-stack-size - analyze-data-layout diff --git a/share/revng/test/configuration/revng-db/import-prototypes-from-db.yml b/share/revng/test/configuration/revng-db/import-prototypes-from-db.yml new file mode 100644 index 000000000..df9c1bed5 --- /dev/null +++ b/share/revng/test/configuration/revng-db/import-prototypes-from-db.yml @@ -0,0 +1,25 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +tags: + - name: import-prototypes-from-db + +sources: + - tags: [import-prototypes-from-db] + prefix: share/revng/test/tests/import-prototypes-from-db + members: + - windows-x86-64.yml + - arch-mismatch.yml + - platform-name.yml + +commands: + - type: revng-db.import-prototypes-from-db-test + from: + - type: source + filter: import-prototypes-from-db + command: |- + revng analyze import-prototypes-from-db /dev/null + --model "$INPUT" + | revng model opt -verify + | FileCheck "${SOURCE}.filecheck" diff --git a/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml b/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml new file mode 100644 index 000000000..9ec7256c4 --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml @@ -0,0 +1,10 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +--- +Architecture: arm +OperatingSystem: Windows +ImportedDynamicFunctions: + - Name: CreateFileW + - Name: ExitProcess diff --git a/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml.filecheck b/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml.filecheck new file mode 100644 index 000000000..e65cf594e --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/arch-mismatch.yml.filecheck @@ -0,0 +1,10 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +# No matching platform for arm/Windows, so no prototypes should be imported +CHECK: - Name:{{.*}}CreateFileW +CHECK-NOT: Prototype: +CHECK: - Name:{{.*}}ExitProcess +CHECK-NOT: Prototype: +CHECK-NOT: TypeDefinitions: diff --git a/share/revng/test/tests/import-prototypes-from-db/platform-name.yml b/share/revng/test/tests/import-prototypes-from-db/platform-name.yml new file mode 100644 index 000000000..d9756196f --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/platform-name.yml @@ -0,0 +1,11 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +--- +Architecture: x86_64 +OperatingSystem: Windows +PlatformName: windows-pdb +ImportedDynamicFunctions: + - Name: ExitProcess + - Name: GetLastError diff --git a/share/revng/test/tests/import-prototypes-from-db/platform-name.yml.filecheck b/share/revng/test/tests/import-prototypes-from-db/platform-name.yml.filecheck new file mode 100644 index 000000000..2b8964dfe --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/platform-name.yml.filecheck @@ -0,0 +1,15 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +# Verify that PlatformName direct lookup works +CHECK: - Name:{{.*}}ExitProcess +CHECK-NEXT: Prototype: +CHECK-NEXT: Kind:{{.*}}DefinedType + +CHECK: - Name:{{.*}}GetLastError +CHECK-NEXT: Prototype: +CHECK-NEXT: Kind:{{.*}}DefinedType + +CHECK: TypeDefinitions: +CHECK: Kind:{{.*}}CABIFunctionDefinition diff --git a/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml b/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml new file mode 100644 index 000000000..012353677 --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml @@ -0,0 +1,12 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +--- +Architecture: x86_64 +OperatingSystem: Windows +ImportedDynamicFunctions: + - Name: CloseHandle + - Name: ExitProcess + - Name: GetLastError + - Name: NonExistentFunction diff --git a/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml.filecheck b/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml.filecheck new file mode 100644 index 000000000..49878caf7 --- /dev/null +++ b/share/revng/test/tests/import-prototypes-from-db/windows-x86-64.yml.filecheck @@ -0,0 +1,24 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +# Verify that typed symbols get prototypes imported +CHECK: - Name:{{.*}}CloseHandle +CHECK-NEXT: Prototype: +CHECK-NEXT: Kind:{{.*}}DefinedType + +CHECK: - Name:{{.*}}ExitProcess +CHECK-NEXT: Prototype: +CHECK-NEXT: Kind:{{.*}}DefinedType + +CHECK: - Name:{{.*}}GetLastError +CHECK-NEXT: Prototype: +CHECK-NEXT: Kind:{{.*}}DefinedType + +# NonExistentFunction should have no prototype +CHECK: - Name:{{.*}}NonExistentFunction +CHECK-NOT: Prototype: +CHECK: TypeDefinitions: + +# Verify that type definitions were imported +CHECK: Kind:{{.*}}CABIFunctionDefinition