mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Introduce ImportPrototypesFromDatabase
This commit introduces an analysis to import prototypes from a SQLite database of well-known prototypes, typically built from debug info of operating systems. This analysis supersedes import-well-known-models.
This commit is contained in:
@@ -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"
|
||||
|
||||
+2
-2
@@ -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);
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<int64_t>())
|
||||
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<std::string> &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<int64_t, int64_t>())
|
||||
PlatformID = ID;
|
||||
|
||||
return PlatformID;
|
||||
}
|
||||
|
||||
/// Enumerate libraries on the given platform that provide at least one of
|
||||
/// the requested symbols.
|
||||
std::vector<LibraryInfo>
|
||||
enumerateLibraries(int64_t PlatformID,
|
||||
const std::vector<std::string> &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<int>(PlatformID));
|
||||
|
||||
std::vector<LibraryInfo> Libraries;
|
||||
for (auto [LibraryID, HeaderText] :
|
||||
Statement.execute<int64_t, llvm::StringRef>())
|
||||
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<std::string> &SymbolNames,
|
||||
TupleTree<model::Binary> &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<int>(Library.LibraryID));
|
||||
|
||||
struct TypeInfo {
|
||||
std::string Body;
|
||||
int64_t OriginalID;
|
||||
std::string Kind;
|
||||
};
|
||||
|
||||
std::vector<TypeInfo> TypeDefinitions;
|
||||
std::map<int64_t, std::string> OriginalIDToKind;
|
||||
|
||||
for (auto [Body, OriginalID] :
|
||||
TypeDefinitionsStatement.execute<llvm::StringRef, int64_t>()) {
|
||||
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<int>(Library.LibraryID));
|
||||
|
||||
struct SymbolInfo {
|
||||
std::string Name;
|
||||
int64_t OriginalID;
|
||||
bool HasType;
|
||||
};
|
||||
|
||||
std::vector<SymbolInfo> Symbols;
|
||||
for (auto [Name, OriginalID] :
|
||||
SymbolsStatement.execute<llvm::StringRef, int64_t>()) {
|
||||
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<model::Binary>::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<model::Binary> &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<model::Binary> &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<std::string> 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<std::vector<pipeline::Kind *>> AcceptedKinds;
|
||||
|
||||
void run(pipeline::ExecutionContext &Context) {
|
||||
TupleTree<model::Binary>
|
||||
&Model = revng::getWritableModelFromContext(Context);
|
||||
PrototypeDatabase::run(Model);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static pipeline::RegisterAnalysis<ImportPrototypesFromDatabasePipelineAnalysis>
|
||||
RegisterImportPrototypesFromDatabase;
|
||||
@@ -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<model::Binary> FromModel;
|
||||
TypeCopier Copier;
|
||||
|
||||
public:
|
||||
WellKnownModel(TupleTree<model::Binary> &&FromModel,
|
||||
TupleTree<model::Binary> &DestinationModel) :
|
||||
FromModel(FromModel), Copier(this->FromModel, DestinationModel) {}
|
||||
};
|
||||
|
||||
class ImportWellKnownModelsAnalysis {
|
||||
public:
|
||||
static constexpr auto Name = "import-well-known-models";
|
||||
|
||||
public:
|
||||
std::vector<std::vector<pipeline::Kind *>> AcceptedKinds;
|
||||
|
||||
public:
|
||||
void run(pipeline::ExecutionContext &Context);
|
||||
};
|
||||
|
||||
} // namespace revng::pipes
|
||||
|
||||
static void importWellKnownModels(TupleTree<model::Binary> &Model) {
|
||||
using namespace revng::pipes;
|
||||
|
||||
std::vector<std::unique_ptr<WellKnownModel>> WellKnownModels;
|
||||
std::map<WellKnownFunctionKey, std::pair<WellKnownModel *, model::Function *>>
|
||||
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<model::Binary>::fromFile(Path));
|
||||
auto NewWKM = std::make_unique<WellKnownModel>(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::Binary> &Model = getWritableModelFromContext(Context);
|
||||
importWellKnownModels(Model);
|
||||
}
|
||||
|
||||
static pipeline::RegisterAnalysis<revng::pipes::ImportWellKnownModelsAnalysis>
|
||||
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
|
||||
@@ -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<VerifyDiff> A2;
|
||||
static RegisterAnalysis<SetModel> A3;
|
||||
static RegisterAnalysis<VerifyModel> A4;
|
||||
static RegisterAnalysis<ParseBinaryAnalysis> A5;
|
||||
static RegisterAnalysis<ImportWellKnownModelsAnalysis> A6;
|
||||
static RegisterAnalysis<ImportPrototypesFromDatabase> A6;
|
||||
static RegisterAnalysis<DetectABI> A7;
|
||||
static RegisterAnalysis<DetectStackSize> A8;
|
||||
static RegisterAnalysis<AnalyzeDataLayout> A9;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user