From 20cdfe668a91ee58b9544add2d3f890da856b401 Mon Sep 17 00:00:00 2001 From: Antonio Frighetto Date: Mon, 31 Jan 2022 10:39:20 +0100 Subject: [PATCH] EFA: take the control-flow graph out of the model The control-flow graph and all its hierarchy components have been moved from `model` to `efa`. The CFG is now serialized onto the LLVM IR module as a metadata. --- .../BasicBlock.h | 22 +- .../revng/EarlyFunctionAnalysis/CallEdge.h | 118 ++++++ .../FunctionEdge.h | 14 +- .../FunctionEdgeBase.h | 17 +- .../FunctionEdgeType.h | 8 +- .../EarlyFunctionAnalysis/FunctionMetadata.h | 47 +++ .../revng/EarlyFunctionAnalysis/IRHelpers.h | 100 +++++ include/revng/Model/Binary.h | 53 --- include/revng/Model/CallEdge.h | 71 ---- include/revng/Model/CallSitePrototype.h | 43 ++ include/revng/Model/Function.h | 13 +- include/revng/Model/IRHelpers.h | 54 --- include/revng/Model/README.md | 4 +- include/revng/Support/IRHelpers.h | 1 + lib/EarlyFunctionAnalysis/CMakeLists.txt | 31 +- ...ollectFunctionsFromUnusedAddressesPass.cpp | 26 +- .../EarlyFunctionAnalysis.cpp | 118 ++++-- .../FunctionMetadata.cpp | 372 ++++++++++++++++++ lib/FunctionIsolation/EnforceABI.cpp | 34 +- lib/FunctionIsolation/IsolateFunctions.cpp | 64 +-- lib/Lift/CodeGenerator.cpp | 1 - lib/Model/Binary.cpp | 338 +--------------- lib/Model/CMakeLists.txt | 6 +- 23 files changed, 923 insertions(+), 632 deletions(-) rename include/revng/{Model => EarlyFunctionAnalysis}/BasicBlock.h (64%) create mode 100644 include/revng/EarlyFunctionAnalysis/CallEdge.h rename include/revng/{Model => EarlyFunctionAnalysis}/FunctionEdge.h (64%) rename include/revng/{Model => EarlyFunctionAnalysis}/FunctionEdgeBase.h (68%) rename include/revng/{Model => EarlyFunctionAnalysis}/FunctionEdgeType.h (87%) create mode 100644 include/revng/EarlyFunctionAnalysis/FunctionMetadata.h create mode 100644 include/revng/EarlyFunctionAnalysis/IRHelpers.h delete mode 100644 include/revng/Model/CallEdge.h create mode 100644 include/revng/Model/CallSitePrototype.h create mode 100644 lib/EarlyFunctionAnalysis/FunctionMetadata.cpp diff --git a/include/revng/Model/BasicBlock.h b/include/revng/EarlyFunctionAnalysis/BasicBlock.h similarity index 64% rename from include/revng/Model/BasicBlock.h rename to include/revng/EarlyFunctionAnalysis/BasicBlock.h index 481880fdc..8ec3650b2 100644 --- a/include/revng/Model/BasicBlock.h +++ b/include/revng/EarlyFunctionAnalysis/BasicBlock.h @@ -4,9 +4,9 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Model/CallEdge.h" -#include "revng/Model/FunctionEdge.h" -#include "revng/Model/FunctionEdgeBase.h" +#include "revng/EarlyFunctionAnalysis/CallEdge.h" +#include "revng/EarlyFunctionAnalysis/FunctionEdge.h" +#include "revng/EarlyFunctionAnalysis/FunctionEdgeBase.h" #include "revng/Model/Identifier.h" #include "revng/Model/VerifyHelper.h" #include "revng/Support/MetaAddress.h" @@ -24,34 +24,30 @@ fields: End address of the basic block, i.e., the address where the last instruction ends type: MetaAddress - - name: CustomName - doc: Optional custom name - type: Identifier - optional: true - name: Successors doc: List of successor edges sequence: type: SortedVector upcastable: true - elementType: model::FunctionEdgeBase + elementType: efa::FunctionEdgeBase key: - Start TUPLE-TREE-YAML */ -#include "revng/Model/Generated/Early/BasicBlock.h" +#include "revng/EarlyFunctionAnalysis/Generated/Early/BasicBlock.h" -class model::BasicBlock : public model::generated::BasicBlock { +class efa::BasicBlock : public efa::generated::BasicBlock { public: using generated::BasicBlock::BasicBlock; public: - Identifier name() const; + model::Identifier name() const; public: bool verify() const debug_function; bool verify(bool Assert) const debug_function; - bool verify(VerifyHelper &VH) const; + bool verify(model::VerifyHelper &VH) const; void dump() const debug_function; }; -#include "revng/Model/Generated/Late/BasicBlock.h" +#include "revng/EarlyFunctionAnalysis/Generated/Late/BasicBlock.h" diff --git a/include/revng/EarlyFunctionAnalysis/CallEdge.h b/include/revng/EarlyFunctionAnalysis/CallEdge.h new file mode 100644 index 000000000..f85002f22 --- /dev/null +++ b/include/revng/EarlyFunctionAnalysis/CallEdge.h @@ -0,0 +1,118 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "revng/ADT/MutableSet.h" +#include "revng/EarlyFunctionAnalysis/FunctionEdgeBase.h" +#include "revng/Model/Binary.h" +#include "revng/Model/FunctionAttribute.h" +#include "revng/Model/VerifyHelper.h" + +/* TUPLE-TREE-YAML +name: CallEdge +doc: A CFG edge to represent function calls (direct, indirect and tail calls) +type: struct +inherits: FunctionEdgeBase +fields: + - name: DynamicFunction + doc: | + Name of the dynamic function being called, or empty if not a dynamic call + type: "std::string" + optional: true + - name: Attributes + doc: | + Attributes for this function + + Note: To have the effective list of attributes for this call site, you + have to add attributes on the called function. + TODO: switch to std::set + sequence: + type: MutableSet + elementType: model::FunctionAttribute::Values + optional: true +TUPLE-TREE-YAML */ + +#include "revng/EarlyFunctionAnalysis/Generated/Early/CallEdge.h" + +class efa::CallEdge : public efa::generated::CallEdge { +private: + static constexpr const FunctionEdgeType::Values + AssociatedType = FunctionEdgeType::FunctionCall; + +public: + CallEdge() : efa::generated::CallEdge() { Type = AssociatedType; } + + CallEdge(MetaAddress Destination, FunctionEdgeType::Values Type) : + efa::generated::CallEdge(Destination, Type) {} + +public: + static bool classof(const FunctionEdgeBase *A) { return classof(A->key()); } + static bool classof(const Key &K) { + return FunctionEdgeType::isCall(std::get<1>(K)); + } + +public: + bool verify() const debug_function; + bool verify(bool Assert) const debug_function; + bool verify(model::VerifyHelper &VH) const; + void dump() const debug_function; +}; + +inline model::TypePath getPrototype(const model::Binary &Binary, + MetaAddress CallerFunctionAddress, + MetaAddress CallerBlockAddress, + const efa::CallEdge &Edge) { + model::TypePath Result; + + auto It = Binary.Functions.at(CallerFunctionAddress) + .CallSitePrototypes.find(CallerBlockAddress); + if (It != Binary.Functions.at(CallerFunctionAddress).CallSitePrototypes.end()) + Result = It->Prototype; + + if (Edge.Type == efa::FunctionEdgeType::FunctionCall) { + if (not Edge.DynamicFunction.empty()) { + // Get the dynamic function prototype + Result = Binary.ImportedDynamicFunctions.at(Edge.DynamicFunction) + .Prototype; + } else if (Edge.Destination.isValid()) { + // Get the function prototype + Result = Binary.Functions.at(Edge.Destination).Prototype; + } else { + revng_abort(); + } + } + + if (not Result.isValid()) + Result = Binary.DefaultPrototype; + + return Result; +} + +inline bool hasAttribute(const model::Binary &Binary, + const efa::CallEdge &Edge, + model::FunctionAttribute::Values Attribute) { + using namespace model; + + if (Edge.Attributes.count(Attribute) != 0) + return true; + + if (Edge.Type == efa::FunctionEdgeType::FunctionCall) { + const MutableSet *CalleeAttributes = nullptr; + if (not Edge.DynamicFunction.empty()) { + const auto &F = Binary.ImportedDynamicFunctions.at(Edge.DynamicFunction); + CalleeAttributes = &F.Attributes; + } else if (Edge.Destination.isValid()) { + CalleeAttributes = &Binary.Functions.at(Edge.Destination).Attributes; + } else { + revng_abort(); + } + + return CalleeAttributes->count(Attribute) != 0; + } + + return false; +} + +#include "revng/EarlyFunctionAnalysis/Generated/Late/CallEdge.h" diff --git a/include/revng/Model/FunctionEdge.h b/include/revng/EarlyFunctionAnalysis/FunctionEdge.h similarity index 64% rename from include/revng/Model/FunctionEdge.h rename to include/revng/EarlyFunctionAnalysis/FunctionEdge.h index dabd1479a..969708280 100644 --- a/include/revng/Model/FunctionEdge.h +++ b/include/revng/EarlyFunctionAnalysis/FunctionEdge.h @@ -4,7 +4,7 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Model/FunctionEdgeBase.h" +#include "revng/EarlyFunctionAnalysis/FunctionEdgeBase.h" #include "revng/Model/VerifyHelper.h" /* TUPLE-TREE-YAML @@ -15,17 +15,17 @@ inherits: FunctionEdgeBase fields: [] TUPLE-TREE-YAML */ -#include "revng/Model/Generated/Early/FunctionEdge.h" +#include "revng/EarlyFunctionAnalysis/Generated/Early/FunctionEdge.h" -class model::FunctionEdge : public model::generated::FunctionEdge { +class efa::FunctionEdge : public efa::generated::FunctionEdge { private: static constexpr const FunctionEdgeType::Values AssociatedType = FunctionEdgeType::DirectBranch; public: - FunctionEdge() : model::generated::FunctionEdge() { Type = AssociatedType; } + FunctionEdge() : efa::generated::FunctionEdge() { Type = AssociatedType; } FunctionEdge(MetaAddress Destination, FunctionEdgeType::Values Type) : - model::generated::FunctionEdge(Destination, Type) {} + efa::generated::FunctionEdge(Destination, Type) {} public: static bool classof(const FunctionEdgeBase *A) { return classof(A->key()); } @@ -35,8 +35,8 @@ public: bool verify() const debug_function; bool verify(bool Assert) const debug_function; - bool verify(VerifyHelper &VH) const; + bool verify(model::VerifyHelper &VH) const; void dump() const debug_function; }; -#include "revng/Model/Generated/Late/FunctionEdge.h" +#include "revng/EarlyFunctionAnalysis/Generated/Late/FunctionEdge.h" diff --git a/include/revng/Model/FunctionEdgeBase.h b/include/revng/EarlyFunctionAnalysis/FunctionEdgeBase.h similarity index 68% rename from include/revng/Model/FunctionEdgeBase.h rename to include/revng/EarlyFunctionAnalysis/FunctionEdgeBase.h index b4b7608ac..5c5ec12f1 100644 --- a/include/revng/Model/FunctionEdgeBase.h +++ b/include/revng/EarlyFunctionAnalysis/FunctionEdgeBase.h @@ -4,14 +4,7 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Model/CABIFunctionType.h" -#include "revng/Model/EnumType.h" -#include "revng/Model/FunctionEdgeType.h" -#include "revng/Model/PrimitiveType.h" -#include "revng/Model/RawFunctionType.h" -#include "revng/Model/StructType.h" -#include "revng/Model/TypedefType.h" -#include "revng/Model/UnionType.h" +#include "revng/EarlyFunctionAnalysis/FunctionEdgeType.h" #include "revng/Model/VerifyHelper.h" #include "revng/Support/MetaAddress.h" #include "revng/Support/MetaAddress/YAMLTraits.h" @@ -40,9 +33,9 @@ key: abstract: true TUPLE-TREE-YAML */ -#include "revng/Model/Generated/Early/FunctionEdgeBase.h" +#include "revng/EarlyFunctionAnalysis/Generated/Early/FunctionEdgeBase.h" -class model::FunctionEdgeBase : public model::generated::FunctionEdgeBase { +class efa::FunctionEdgeBase : public efa::generated::FunctionEdgeBase { public: using generated::FunctionEdgeBase::FunctionEdgeBase; @@ -52,8 +45,8 @@ public: bool verify() const debug_function; bool verify(bool Assert) const debug_function; - bool verify(VerifyHelper &VH) const; + bool verify(model::VerifyHelper &VH) const; void dump() const debug_function; }; -#include "revng/Model/Generated/Late/FunctionEdgeBase.h" +#include "revng/EarlyFunctionAnalysis/Generated/Late/FunctionEdgeBase.h" diff --git a/include/revng/Model/FunctionEdgeType.h b/include/revng/EarlyFunctionAnalysis/FunctionEdgeType.h similarity index 87% rename from include/revng/Model/FunctionEdgeType.h rename to include/revng/EarlyFunctionAnalysis/FunctionEdgeType.h index 2368de1c4..9d93619b2 100644 --- a/include/revng/Model/FunctionEdgeType.h +++ b/include/revng/EarlyFunctionAnalysis/FunctionEdgeType.h @@ -35,11 +35,11 @@ members: doc: The basic block ends with an unreachable instruction TUPLE-TREE-YAML */ -#include "revng/Model/Generated/Early/FunctionEdgeType.h" +#include "revng/EarlyFunctionAnalysis/Generated/Early/FunctionEdgeType.h" // TODO: we need to handle noreturn function calls -namespace model::FunctionEdgeType { +namespace efa::FunctionEdgeType { inline bool isCall(Values V) { switch (V) { case Count: @@ -64,6 +64,6 @@ inline bool isCall(Values V) { } } -} // namespace model::FunctionEdgeType +} // namespace efa::FunctionEdgeType -#include "revng/Model/Generated/Late/FunctionEdgeType.h" +#include "revng/EarlyFunctionAnalysis/Generated/Late/FunctionEdgeType.h" diff --git a/include/revng/EarlyFunctionAnalysis/FunctionMetadata.h b/include/revng/EarlyFunctionAnalysis/FunctionMetadata.h new file mode 100644 index 000000000..0393d0d0d --- /dev/null +++ b/include/revng/EarlyFunctionAnalysis/FunctionMetadata.h @@ -0,0 +1,47 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "revng/EarlyFunctionAnalysis/BasicBlock.h" +#include "revng/Model/VerifyHelper.h" +#include "revng/Support/MetaAddress.h" +#include "revng/Support/MetaAddress/YAMLTraits.h" +#include "revng/Support/YAMLTraits.h" +#include "revng/TupleTree/TupleTree.h" + +/* TUPLE-TREE-YAML +name: FunctionMetadata +doc: "Metadata attached to a function. As of now, it includes a list of basic +blocks, representing the control-flow graph. It is meant to include further +information in the future." +type: struct +fields: + - name: Entry + doc: Start address of the basic block + type: MetaAddress + - name: ControlFlowGraph + sequence: + type: SortedVector + elementType: efa::BasicBlock + optional: true +key: + - Entry +TUPLE-TREE-YAML */ + +#include "revng/EarlyFunctionAnalysis/Generated/Early/FunctionMetadata.h" + +class efa::FunctionMetadata : public efa::generated::FunctionMetadata { +public: + using generated::FunctionMetadata::FunctionMetadata; + +public: + bool verify(const model::Binary &Binary) const debug_function; + bool verify(const model::Binary &Binary, bool Assert) const debug_function; + bool verify(const model::Binary &Binary, model::VerifyHelper &VH) const; + void dump() const debug_function; + void dumpCFG(const model::Binary &Binary) const debug_function; +}; + +#include "revng/EarlyFunctionAnalysis/Generated/Late/FunctionMetadata.h" diff --git a/include/revng/EarlyFunctionAnalysis/IRHelpers.h b/include/revng/EarlyFunctionAnalysis/IRHelpers.h new file mode 100644 index 000000000..175d2a1f7 --- /dev/null +++ b/include/revng/EarlyFunctionAnalysis/IRHelpers.h @@ -0,0 +1,100 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/IR/Instructions.h" + +#include "revng/EarlyFunctionAnalysis/FunctionMetadata.h" +#include "revng/Model/Binary.h" +#include "revng/Model/IRHelpers.h" +#include "revng/Support/Assert.h" +#include "revng/Support/IRHelpers.h" +#include "revng/Support/MetaAddress.h" +#include "revng/TupleTree/TupleTree.h" + +namespace detail { + +inline TupleTree +extractFunctionMetadata(llvm::MDNode *MD) { + using namespace llvm; + + efa::FunctionMetadata FM; + const MDOperand &Op = MD->getOperand(0); + revng_assert(MD != nullptr && isa(Op)); + + StringRef YAMLString = cast(Op)->getString(); + auto MaybeParsed = TupleTree::deserialize(YAMLString); + revng_assert(MaybeParsed); + MaybeParsed->verify(); + return std::move(MaybeParsed.get()); +} + +} // namespace detail + +inline TupleTree +extractFunctionMetadata(const llvm::Function *F) { + auto *MDNode = F->getMetadata(FunctionMetadataMDName); + return detail::extractFunctionMetadata(MDNode); +} + +inline TupleTree +extractFunctionMetadata(llvm::BasicBlock *BB) { + auto *MDNode = BB->getTerminator()->getMetadata(FunctionMetadataMDName); + return detail::extractFunctionMetadata(MDNode); +} + +/// \brief Given a Call instruction and the model type of its parent function, +/// return the edge on the model that represents that call (std::nullopt +/// if this doesn't exist) and the MetaAddress associated to the +/// call-site. +inline std::pair, MetaAddress> +getCallEdge(const model::Binary &Binary, const llvm::CallInst *Call) { + using namespace llvm; + + MetaAddress BlockAddress = getMetaAddressMetadata(Call, + CallerBlockStartMDName); + if (BlockAddress.isInvalid()) + return { std::nullopt, BlockAddress }; + + auto *ParentFunction = Call->getParent()->getParent(); + efa::FunctionMetadata FM = *extractFunctionMetadata(ParentFunction).get(); + efa::BasicBlock Block = FM.ControlFlowGraph.at(BlockAddress); + + // Find the call edge + efa::CallEdge *ModelCall = nullptr; + for (auto &Edge : Block.Successors) { + if (auto *CE = dyn_cast(Edge.get())) { + revng_assert(ModelCall == nullptr); + ModelCall = CE; + } + } + revng_assert(ModelCall != nullptr); + + return { *ModelCall, Block.Start }; +} + +/// \return the prototype associated to a CallInst. +/// +/// \note If the model type of the parent function is not provided, this will be +/// deduced using the Call instruction's parent function. +/// +/// \note If the callsite has no associated prototype, e.g. the called functions +/// is not an isolated function, a null pointer is returned. +inline model::TypePath +getCallSitePrototype(const model::Binary &Binary, + const llvm::CallInst *Call, + const model::Function *ParentFunction = nullptr) { + if (not ParentFunction) + ParentFunction = llvmToModelFunction(Binary, *Call->getFunction()); + + if (not ParentFunction) + return {}; + + const auto &[Edge, BlockAddress] = getCallEdge(Binary, Call); + if (not Edge) + return {}; + + return getPrototype(Binary, ParentFunction->Entry, BlockAddress, *Edge); +} diff --git a/include/revng/Model/Binary.h b/include/revng/Model/Binary.h index 7ea8dc319..c2297bda4 100644 --- a/include/revng/Model/Binary.h +++ b/include/revng/Model/Binary.h @@ -122,59 +122,6 @@ public: bool verify(VerifyHelper &VH) const; void dump() const debug_function; std::string toString() const debug_function; - -public: - void dumpCFG(const Function &F) const debug_function; }; -inline model::TypePath -getPrototype(const model::Binary &Binary, const model::CallEdge &Edge) { - model::TypePath Result; - - if (Edge.Type == model::FunctionEdgeType::FunctionCall) { - if (not Edge.DynamicFunction.empty()) { - // Get the dynamic function prototype - Result = Binary.ImportedDynamicFunctions.at(Edge.DynamicFunction) - .Prototype; - } else if (Edge.Destination.isValid()) { - // Get the function prototype - Result = Binary.Functions.at(Edge.Destination).Prototype; - } else { - revng_abort(); - } - } else { - Result = Edge.Prototype; - } - - if (not Result.isValid()) - Result = Binary.DefaultPrototype; - - return Result; -} - -inline bool hasAttribute(const model::Binary &Binary, - const model::CallEdge &Edge, - model::FunctionAttribute::Values Attribute) { - using namespace model; - - if (Edge.Attributes.count(Attribute) != 0) - return true; - - if (Edge.Type == FunctionEdgeType::FunctionCall) { - const MutableSet *CalleeAttributes = nullptr; - if (not Edge.DynamicFunction.empty()) { - const auto &F = Binary.ImportedDynamicFunctions.at(Edge.DynamicFunction); - CalleeAttributes = &F.Attributes; - } else if (Edge.Destination.isValid()) { - CalleeAttributes = &Binary.Functions.at(Edge.Destination).Attributes; - } else { - revng_abort(); - } - - return CalleeAttributes->count(Attribute) != 0; - } - - return false; -} - #include "revng/Model/Generated/Late/Binary.h" diff --git a/include/revng/Model/CallEdge.h b/include/revng/Model/CallEdge.h deleted file mode 100644 index 64f1f007f..000000000 --- a/include/revng/Model/CallEdge.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -#include "revng/ADT/MutableSet.h" -#include "revng/Model/FunctionAttribute.h" -#include "revng/Model/FunctionEdgeBase.h" -#include "revng/Model/VerifyHelper.h" - -/* TUPLE-TREE-YAML -name: CallEdge -doc: A CFG edge to represent function calls (direct, indirect and tail calls) -type: struct -inherits: FunctionEdgeBase -fields: - - name: Prototype - doc: | - Path to the prototype for this call site - - In case of a direct function call, it has to be invalid. - reference: - pointeeType: "model::Type" - rootType: "model::Binary" - - name: DynamicFunction - doc: | - Name of the dynamic function being called, or empty if not a dynamic call - type: "std::string" - optional: true - - name: Attributes - doc: | - Attributes for this function - - Note: To have the effective list of attributes for this call site, you - have to add attributes on the called function. - TODO: switch to std::set - sequence: - type: MutableSet - elementType: model::FunctionAttribute::Values - optional: true -TUPLE-TREE-YAML */ - -#include "revng/Model/Generated/Early/CallEdge.h" - -class model::CallEdge : public model::generated::CallEdge { -private: - static constexpr const FunctionEdgeType::Values - AssociatedType = FunctionEdgeType::FunctionCall; - -public: - CallEdge() : model::generated::CallEdge() { Type = AssociatedType; } - - CallEdge(MetaAddress Destination, FunctionEdgeType::Values Type) : - model::generated::CallEdge(Destination, Type) {} - -public: - static bool classof(const FunctionEdgeBase *A) { return classof(A->key()); } - static bool classof(const Key &K) { - return FunctionEdgeType::isCall(std::get<1>(K)); - } - -public: - bool verify() const debug_function; - bool verify(bool Assert) const debug_function; - bool verify(VerifyHelper &VH) const; - void dump() const debug_function; - bool isDirect() const { return not Prototype.isValid(); } -}; - -#include "revng/Model/Generated/Late/CallEdge.h" diff --git a/include/revng/Model/CallSitePrototype.h b/include/revng/Model/CallSitePrototype.h new file mode 100644 index 000000000..276f0f7fe --- /dev/null +++ b/include/revng/Model/CallSitePrototype.h @@ -0,0 +1,43 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "revng/Model/Type.h" +#include "revng/Support/MetaAddress.h" +#include "revng/Support/MetaAddress/YAMLTraits.h" +#include "revng/TupleTree/TupleTree.h" + +/* TUPLE-TREE-YAML +name: CallSitePrototype +doc: Prototype of a callsite +type: struct +fields: + - name: CallerBlockAddress + doc: Address of the basic block of the call + type: MetaAddress + - name: Prototype + doc: Prototype + reference: + pointeeType: model::Type + rootType: model::Binary +key: + - CallerBlockAddress +TUPLE-TREE-YAML */ + +#include "revng/Model/Generated/Early/CallSitePrototype.h" + +class model::CallSitePrototype : public model::generated::CallSitePrototype { +public: + using generated::CallSitePrototype::CallSitePrototype; + +public: + bool verify() const debug_function; + bool verify(bool Assert) const debug_function; + bool verify(VerifyHelper &VH) const; + void dump() const debug_function; + bool isDirect() const { return not Prototype.isValid(); } +}; + +#include "revng/Model/Generated/Late/CallSitePrototype.h" diff --git a/include/revng/Model/Function.h b/include/revng/Model/Function.h index 44709c820..9d1f5d136 100644 --- a/include/revng/Model/Function.h +++ b/include/revng/Model/Function.h @@ -6,7 +6,7 @@ #include "revng/ADT/MutableSet.h" #include "revng/ADT/SortedVector.h" -#include "revng/Model/BasicBlock.h" +#include "revng/Model/CallSitePrototype.h" #include "revng/Model/FunctionAttribute.h" #include "revng/Model/FunctionType.h" #include "revng/Model/Identifier.h" @@ -38,12 +38,6 @@ fields: - name: Type doc: Type of the function type: model::FunctionType::Values - - name: CFG - doc: List of basic blocks, which represent the CFG - sequence: - type: SortedVector - elementType: model::BasicBlock - optional: true - name: StackFrameType doc: The type of the stack frame reference: @@ -62,6 +56,11 @@ fields: type: MutableSet elementType: model::FunctionAttribute::Values optional: true + - name: CallSitePrototypes + sequence: + type: SortedVector + elementType: model::CallSitePrototype + optional: true key: - Entry TUPLE-TREE-YAML */ diff --git a/include/revng/Model/IRHelpers.h b/include/revng/Model/IRHelpers.h index ee26c72c0..0a804e1bb 100644 --- a/include/revng/Model/IRHelpers.h +++ b/include/revng/Model/IRHelpers.h @@ -11,34 +11,6 @@ #include "revng/Support/IRHelpers.h" #include "revng/Support/MetaAddress.h" -/// \brief Given a Call instruction and the model type of its parent function, -/// return the edge on the model that represents that call, or nullptr if -/// this doesn't exist. -inline model::CallEdge *getCallEdge(const model::Binary &Binary, - const model::Function &Function, - const llvm::CallInst *Call) { - using namespace llvm; - - MetaAddress BlockAddress = getMetaAddressMetadata(Call, - CallerBlockStartMDName); - if (BlockAddress.isInvalid()) - return nullptr; - - auto &Successors = Function.CFG.at(BlockAddress).Successors; - - // Find the call edge - model::CallEdge *ModelCall = nullptr; - for (auto &Edge : Successors) { - if (auto *CE = dyn_cast(Edge.get())) { - revng_assert(ModelCall == nullptr); - ModelCall = CE; - } - } - revng_assert(ModelCall != nullptr); - - return ModelCall; -} - inline MetaAddress getMetaAddressOfIsolatedFunction(const llvm::Function &F) { revng_assert(FunctionTags::Isolated.isTagOf(&F)); return getMetaAddressMetadata(&F, FunctionEntryMDNName); @@ -67,29 +39,3 @@ llvmToModelFunction(const model::Binary &Binary, const llvm::Function &F) { return nullptr; } - -/// \return the prototype associated to a CallInst. -/// -/// \note If the model type of the parent function is not provided, this will be -/// deduced using the Call instruction's parent function. -/// -/// \note If the callsite has no associated prototype, e.g. the called functions -/// is not an isolated function, a null pointer is returned. -inline model::Type * -getCallSitePrototype(const model::Binary &Binary, - const llvm::CallInst *Call, - const model::Function *ParentFunction = nullptr) { - if (not ParentFunction) - ParentFunction = llvmToModelFunction(Binary, *Call->getFunction()); - - if (not ParentFunction) - return nullptr; - - const model::CallEdge *ModelCallEdge = getCallEdge(Binary, - *ParentFunction, - Call); - if (not ModelCallEdge) - return nullptr; - - return getPrototype(Binary, *ModelCallEdge).get(); -} diff --git a/include/revng/Model/README.md b/include/revng/Model/README.md index 55fedf603..b9a30a794 100644 --- a/include/revng/Model/README.md +++ b/include/revng/Model/README.md @@ -126,10 +126,10 @@ fileds: sequence: type: std::vector upcastable: true - elementType: model::FunctionEdgeBase + elementType: efa::FunctionEdgeBase ``` -This will get translated in C++ as `std::vector>`. +This will get translated in C++ as `std::vector>`. #### Reference members diff --git a/include/revng/Support/IRHelpers.h b/include/revng/Support/IRHelpers.h index 24059bdba..32462c10b 100644 --- a/include/revng/Support/IRHelpers.h +++ b/include/revng/Support/IRHelpers.h @@ -61,6 +61,7 @@ inline void eraseFromParent(llvm::Value *V) { constexpr const char *FunctionEntryMDNName = "revng.function.entry"; constexpr const char *CallerBlockStartMDName = "revng.callerblock.start"; constexpr const char *JTReasonMDName = "revng.jt.reasons"; +constexpr const char *FunctionMetadataMDName = "revng.function.metadata"; template inline bool contains(T Range, typename T::value_type V) { diff --git a/lib/EarlyFunctionAnalysis/CMakeLists.txt b/lib/EarlyFunctionAnalysis/CMakeLists.txt index 2f6c3dd95..0327155d4 100644 --- a/lib/EarlyFunctionAnalysis/CMakeLists.txt +++ b/lib/EarlyFunctionAnalysis/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(ABIAnalyses) +# Define revngEarlyFunctionAnalysis library revng_add_analyses_library_internal( revngEarlyFunctionAnalysis AAWriterPass.cpp @@ -13,7 +14,9 @@ revng_add_analyses_library_internal( SegregateDirectStackAccesses.cpp EarlyFunctionAnalysis.cpp CollectFunctionsFromCalleesPass.cpp - CollectFunctionsFromUnusedAddressesPass.cpp) + CollectFunctionsFromUnusedAddressesPass.cpp + FunctionMetadata.cpp + ${GENERATED_IMPLS}) llvm_map_components_to_libnames( LLVM_LIBRARIES @@ -32,3 +35,29 @@ target_link_libraries( revngSupport revngModel ${LLVM_LIBRARIES}) + +# Header files containing efa YAML must be added here +set(EFA_HEADERS_DIR "${CMAKE_SOURCE_DIR}/include/revng/EarlyFunctionAnalysis") +set(OUTPUT_DIR + "${CMAKE_BINARY_DIR}/include/revng/EarlyFunctionAnalysis/Generated") +set(SCHEMA_PATH "${CMAKE_BINARY_DIR}/efa-schema.yml") +target_tuple_tree_generator( + revngEarlyFunctionAnalysis + INSTALL + HEADER_DIRECTORY + EarlyFunctionAnalysis + NAMESPACE + efa + SCHEMA_PATH + ${SCHEMA_PATH} + SEPARATE_STRING_TYPES + "MetaAddress" + HEADERS + "${EFA_HEADERS_DIR}/BasicBlock.h" + "${EFA_HEADERS_DIR}/CallEdge.h" + "${EFA_HEADERS_DIR}/FunctionEdge.h" + "${EFA_HEADERS_DIR}/FunctionEdgeBase.h" + "${EFA_HEADERS_DIR}/FunctionEdgeType.h" + "${EFA_HEADERS_DIR}/FunctionMetadata.h") + +install(DIRECTORY ${OUTPUT_DIR} DESTINATION include/revng/EarlyFunctionAnalysis) diff --git a/lib/EarlyFunctionAnalysis/CollectFunctionsFromUnusedAddressesPass.cpp b/lib/EarlyFunctionAnalysis/CollectFunctionsFromUnusedAddressesPass.cpp index 769213d33..687bb9497 100644 --- a/lib/EarlyFunctionAnalysis/CollectFunctionsFromUnusedAddressesPass.cpp +++ b/lib/EarlyFunctionAnalysis/CollectFunctionsFromUnusedAddressesPass.cpp @@ -8,6 +8,7 @@ #include "llvm/IR/Module.h" #include "revng/EarlyFunctionAnalysis/CollectFunctionsFromUnusedAddressesPass.h" +#include "revng/EarlyFunctionAnalysis/IRHelpers.h" using CFFUAWrapperPass = CollectFunctionsFromUnusedAddressesWrapperPass; char CFFUAWrapperPass::ID = 0; @@ -30,14 +31,34 @@ public: void run(); private: + void loadAllCFGs(); void collectFunctionsFromUnusedAddresses(); private: llvm::Module &M; GeneratedCodeBasicInfo &GCBI; model::Binary &Binary; + SortedVector VisitedBlocks; }; +void CFFUAImpl::loadAllCFGs() { + for (auto &Function : Binary.Functions) { + if (Function.Type == model::FunctionType::Fake) + continue; + + llvm::BasicBlock *Entry = GCBI.getBlockAt(Function.Entry); + llvm::Instruction *Term = Entry->getTerminator(); + auto *FMMDNode = Term->getMetadata(FunctionMetadataMDName); + // CFG not serialized for this function? Skip it + if (not FMMDNode) + continue; + + efa::FunctionMetadata FM = *extractFunctionMetadata(Entry).get(); + for (const efa::BasicBlock &Block : FM.ControlFlowGraph) + VisitedBlocks.insert(Block.Start); + } +} + void CFFUAImpl::collectFunctionsFromUnusedAddresses() { using namespace llvm; Function &Root = *M.getFunction("root"); @@ -56,12 +77,14 @@ void CFFUAImpl::collectFunctionsFromUnusedAddresses() { bool IsPCStore = hasReason(Reasons, JTReason::PCStore); bool IsReturnAddress = hasReason(Reasons, JTReason::ReturnAddress); bool IsLoadAddress = hasReason(Reasons, JTReason::LoadAddress); + bool IsNotPartOfOtherCFG = VisitedBlocks.count(Entry) == 0; // Do not consider addresses found in .rodata that are part of jump // tables of a function. if (not IsLoadAddress and (IsUnusedGlobalData - or (IsMemoryStore and not IsPCStore and not IsReturnAddress))) { + or (IsMemoryStore and not IsPCStore and not IsReturnAddress)) + and IsNotPartOfOtherCFG) { // TODO: keep IsReturnAddress? // Consider addresses found in global data that have not been used or // addresses that are not return addresses and do not end up in the PC @@ -74,6 +97,7 @@ void CFFUAImpl::collectFunctionsFromUnusedAddresses() { } void CFFUAImpl::run() { + loadAllCFGs(); collectFunctionsFromUnusedAddresses(); } diff --git a/lib/EarlyFunctionAnalysis/EarlyFunctionAnalysis.cpp b/lib/EarlyFunctionAnalysis/EarlyFunctionAnalysis.cpp index eb4a7084e..d1f896285 100644 --- a/lib/EarlyFunctionAnalysis/EarlyFunctionAnalysis.cpp +++ b/lib/EarlyFunctionAnalysis/EarlyFunctionAnalysis.cpp @@ -52,6 +52,7 @@ #include "revng/BasicAnalyses/RemoveHelperCalls.h" #include "revng/BasicAnalyses/RemoveNewPCCalls.h" #include "revng/EarlyFunctionAnalysis/EarlyFunctionAnalysis.h" +#include "revng/EarlyFunctionAnalysis/FunctionMetadata.h" #include "revng/Model/Architecture.h" #include "revng/Model/Binary.h" #include "revng/Model/NamedTypedRegister.h" @@ -75,7 +76,7 @@ using llvm::RegisterPass; using llvm::SmallVectorImpl; using llvm::Type; -using FunctionEdgeTypeValue = model::FunctionEdgeType::Values; +using FunctionEdgeTypeValue = efa::FunctionEdgeType::Values; using FunctionTypeValue = model::FunctionType::Values; using GCBI = GeneratedCodeBasicInfo; @@ -144,7 +145,7 @@ public: model::FunctionType::Values Type; std::set ClobberedRegisters; ABIAnalyses::ABIAnalysesResults ABIResults; - SortedVector CFG; + SortedVector CFG; std::optional ElectedFSO; llvm::Function *FakeFunction; @@ -152,7 +153,7 @@ public: FunctionSummary(model::FunctionType::Values Type, std::set ClobberedRegisters, ABIAnalyses::ABIAnalysesResults ABIResults, - SortedVector CFG, + SortedVector CFG, std::optional ElectedFSO, llvm::Function *FakeFunction) : Type(Type), @@ -382,7 +383,7 @@ public: private: OutlinedFunction outlineFunction(llvm::BasicBlock *BB); void integrateFunctionCallee(llvm::BasicBlock *BB, MetaAddress); - SortedVector collectDirectCFG(OutlinedFunction *F); + SortedVector collectDirectCFG(OutlinedFunction *F); void initMarkersForABI(OutlinedFunction *F, llvm::SmallVectorImpl &, llvm::IRBuilder<> &); @@ -394,7 +395,7 @@ private: void materializePCValues(llvm::Function *F, llvm::IRBuilder<> &); void runOptimizationPipeline(llvm::Function *F); FunctionSummary milkInfo(OutlinedFunction *F, - SortedVector &, + SortedVector &, ABIAnalyses::ABIAnalysesResults &, const std::set &); llvm::Function *createFakeFunction(llvm::BasicBlock *BB); @@ -474,6 +475,36 @@ FEA::FunctionEntrypointAnalyzer(llvm::Module &M, } } +static void serializeFunctionMetadata(llvm::LLVMContext &Context, + GeneratedCodeBasicInfo &GCBI, + const FunctionAnalysisResults &Properties, + const TupleTree &Binary) { + using namespace llvm; + + for (const auto &Function : Binary->Functions) { + if (Function.Type == FunctionTypeValue::Invalid + || Function.Type == FunctionTypeValue::Fake) + continue; + + auto &CFG = Properties.at(Function.Entry).CFG; + BasicBlock *BB = GCBI.getBlockAt(Function.Entry); + std::string Buffer; + { + efa::FunctionMetadata FM(Function.Entry); + raw_string_ostream Stream(Buffer); + for (efa::BasicBlock Edge : CFG) + FM.ControlFlowGraph.insert(Edge); + + FM.verify(*Binary, true); + serialize(Stream, FM); + } + + Instruction *Term = BB->getTerminator(); + MDNode *Node = MDNode::get(Context, MDString::get(Context, Buffer)); + Term->setMetadata(FunctionMetadataMDName, Node); + } +} + FunctionSummary FunctionEntrypointAnalyzer::importPrototype(model::FunctionType::Values Type, model::TypePath Prototype) { @@ -550,7 +581,7 @@ static UpcastablePointer buildPrototype(GeneratedCodeBasicInfo &GCBI, model::Binary &Binary, const FunctionSummary &Summary, - const model::BasicBlock &Block) { + const efa::BasicBlock &Block) { using namespace model; using RegisterState = abi::RegisterState::Values; @@ -722,36 +753,42 @@ finalizeModel(GeneratedCodeBasicInfo &GCBI, } } - if (FunctionEdgeType::isCall(Edge->Type)) { - auto *CE = llvm::cast(Edge.get()); + if (efa::FunctionEdgeType::isCall(Edge->Type)) { + auto *CE = llvm::cast(Edge.get()); const auto IDF = Binary.ImportedDynamicFunctions; bool IsDynamicCall = (not SymbolName.empty() and IDF.count(SymbolName.str()) != 0); if (IsDynamicCall) { // It's a dynamic function call - revng_assert(CE->Type == model::FunctionEdgeType::FunctionCall); + revng_assert(CE->Type == efa::FunctionEdgeType::FunctionCall); CE->Destination = MetaAddress::invalid(); CE->DynamicFunction = SymbolName.str(); - // The prototype is implicitly the one of the callee - revng_assert(not CE->Prototype.isValid()); + // The prototype must not exist among the ones of the call sites of + // the function, it is implicitly the one of the callee. + revng_assert(not Function->CallSitePrototypes.count(Block.Start)); } else if (CE->Destination.isValid()) { // It's a simple direct function call - revng_assert(CE->Type == model::FunctionEdgeType::FunctionCall); + revng_assert(CE->Type == efa::FunctionEdgeType::FunctionCall); - // The prototype is implicitly the one of the callee - revng_assert(not CE->Prototype.isValid()); + // The prototype must not exist among the ones of the call sites of + // the function, it is implicitly the one of the callee. + revng_assert(not Function->CallSitePrototypes.count(Block.Start)); } else { // It's an indirect call: forge a new prototype auto Prototype = buildPrototype(GCBI, Binary, Summary, Block); - CE->Prototype = Binary.recordNewType(std::move(Prototype)); + auto TypedPrototype = Binary.recordNewType(std::move(Prototype)); + auto PrototypeInserter = Function->CallSitePrototypes + .batch_insert(); + PrototypeInserter.insert({ Block.Start, TypedPrototype }); } } } } - Function->CFG = Summary.CFG; + efa::FunctionMetadata FM(Function->Entry, Summary.CFG); + FM.verify(Binary, true); } revng_check(Binary.verify(true)); @@ -797,15 +834,15 @@ static std::optional electFSO(const auto &MaybeReturns) { return It->second; } -static UpcastablePointer -makeEdge(MetaAddress Destination, model::FunctionEdgeType::Values Type) { - model::FunctionEdge *Result = nullptr; - using ReturnType = UpcastablePointer; +static UpcastablePointer +makeEdge(MetaAddress Destination, efa::FunctionEdgeType::Values Type) { + efa::FunctionEdge *Result = nullptr; + using ReturnType = UpcastablePointer; - if (model::FunctionEdgeType::isCall(Type)) - return ReturnType::make(Destination, Type); + if (efa::FunctionEdgeType::isCall(Type)) + return ReturnType::make(Destination, Type); else - return ReturnType::make(Destination, Type); + return ReturnType::make(Destination, Type); }; static MetaAddress getFinalAddressOfBasicBlock(llvm::BasicBlock *BB) { @@ -813,16 +850,16 @@ static MetaAddress getFinalAddressOfBasicBlock(llvm::BasicBlock *BB) { return End + Size; } -SortedVector +SortedVector FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { using namespace llvm; - SortedVector CFG; + SortedVector CFG; for (BasicBlock &BB : *F->F) { if (GCBI::isJumpTarget(&BB)) { MetaAddress Start = getBasicBlockPC(&BB); - model::BasicBlock Block{ Start }; + efa::BasicBlock Block{ Start }; Block.End = getFinalAddressOfBasicBlock(&BB); OnceQueue Queue; @@ -830,7 +867,7 @@ FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { // A JT with no successors? if (isa(BB.getTerminator())) { - auto Type = model::FunctionEdgeType::Unreachable; + auto Type = efa::FunctionEdgeType::Unreachable; Block.Successors.insert(makeEdge(MetaAddress::invalid(), Type)); } @@ -845,13 +882,14 @@ FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { if (GCBI::isJumpTarget(Succ)) { MetaAddress Destination = getBasicBlockPC(Succ); auto Edge = makeEdge(Destination, - model::FunctionEdgeType::DirectBranch); + efa::FunctionEdgeType::DirectBranch); Block.Successors.insert(Edge); } else if (F->UnexpectedPCCloned == Succ && succ_size(Current) == 1) { // Need to create an edge only when `unexpectedpc` is the unique // successor of the current basic block. - Block.Successors.insert(makeEdge(MetaAddress::invalid(), - model::FunctionEdgeType::LongJmp)); + auto Edge = makeEdge(MetaAddress::invalid(), + efa::FunctionEdgeType::LongJmp); + Block.Successors.insert(Edge); } else { Instruction *I = &(*Succ->begin()); @@ -860,7 +898,7 @@ FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { revng_assert(Succ->getInstList().size() == 1); } else if (auto *Call = getCallTo(I, PreHookMarker.F)) { MetaAddress Destination; - model::FunctionEdgeType::Values Type; + efa::FunctionEdgeType::Values Type; auto *CalleePC = Call->getArgOperand(1); // Direct or indirect call? @@ -870,20 +908,20 @@ FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { auto It = Binary->Functions.find(AddressPC); if (It != Binary->Functions.end()) { Destination = AddressPC; - Type = model::FunctionEdgeType::FunctionCall; + Type = efa::FunctionEdgeType::FunctionCall; } else { Destination = MetaAddress::invalid(); - Type = model::FunctionEdgeType::IndirectCall; + Type = efa::FunctionEdgeType::IndirectCall; } } else { Destination = MetaAddress::invalid(); - Type = model::FunctionEdgeType::IndirectCall; + Type = efa::FunctionEdgeType::IndirectCall; } auto Edge = makeEdge(Destination, Type); auto DestTy = Oracle.getFunctionType(Destination); if (DestTy == FunctionTypeValue::NoReturn) { - auto *CE = cast(Edge.get()); + auto *CE = cast(Edge.get()); CE->Attributes.insert(model::FunctionAttribute::NoReturn); } @@ -897,7 +935,7 @@ FunctionEntrypointAnalyzer::collectDirectCFG(OutlinedFunction *F) { auto Destination = getBasicBlockPC(Succ); auto Edge = makeEdge(Destination, - model::FunctionEdgeType::FakeFunctionCall); + efa::FunctionEdgeType::FakeFunctionCall); Block.Successors.insert(Edge); } else { Queue.insert(Succ); @@ -1389,7 +1427,7 @@ intersect(const std::set &First, FunctionSummary FEA::milkInfo(OutlinedFunction *OutlinedFunction, - SortedVector &CFG, + SortedVector &CFG, ABIAnalyses::ABIAnalysesResults &ABIResults, const std::set &WrittenRegisters) { using namespace llvm; @@ -1499,7 +1537,7 @@ FEA::milkInfo(OutlinedFunction *OutlinedFunction, // Finalize CFG for the model for (const auto &[CI, EdgeType] : IBIResult) { auto PC = MetaAddress::fromConstant(CI->getArgOperand(2)); - model::BasicBlock &Block = CFG.at(PC); + efa::BasicBlock &Block = CFG.at(PC); Block.Successors.insert(makeEdge(MetaAddress::invalid(), EdgeType)); } @@ -1972,6 +2010,10 @@ bool EarlyFunctionAnalysis::runOnModule(Module &M) { // Finalize model finalizeModel(GCBI, ABICSVs, Properties, *Binary); + // Serialize function metadata, including the CFG, to BB entry points in + // `root` + serializeFunctionMetadata(M.getContext(), GCBI, Properties, Binary); + return false; } diff --git a/lib/EarlyFunctionAnalysis/FunctionMetadata.cpp b/lib/EarlyFunctionAnalysis/FunctionMetadata.cpp new file mode 100644 index 000000000..fb3cbc548 --- /dev/null +++ b/lib/EarlyFunctionAnalysis/FunctionMetadata.cpp @@ -0,0 +1,372 @@ +/// \file FunctionMetadata.cpp + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/raw_os_ostream.h" + +#include "revng/ADT/GenericGraph.h" +#include "revng/EarlyFunctionAnalysis/FunctionMetadata.h" +#include "revng/Model/Binary.h" + +using namespace llvm; + +namespace efa { + +struct FunctionCFGNodeData { + FunctionCFGNodeData(MetaAddress Start) : Start(Start) {} + MetaAddress Start; +}; + +using FunctionCFGNode = ForwardNode; + +/// Graph data structure to represent the CFG for verification purposes +struct FunctionCFG : public GenericGraph { +private: + MetaAddress Entry; + std::map Map; + +public: + FunctionCFG(MetaAddress Entry) : Entry(Entry) {} + +public: + MetaAddress entry() const { return Entry; } + FunctionCFGNode *entryNode() const { return Map.at(Entry); } + +public: + FunctionCFGNode *get(MetaAddress MA) { + FunctionCFGNode *Result = nullptr; + auto It = Map.find(MA); + if (It == Map.end()) { + Result = addNode(MA); + Map[MA] = Result; + } else { + Result = It->second; + } + + return Result; + } + + bool allNodesAreReachable() const { + if (Map.size() == 0) + return true; + + // Ensure all the nodes are reachable from the entry node + df_iterator_default_set Visited; + for (auto &Ignore : depth_first_ext(entryNode(), Visited)) + ; + return Visited.size() == size(); + } + + bool hasOnlyInvalidExits() const { + for (auto &[Address, Node] : Map) + if (Address.isValid() and not Node->hasSuccessors()) + return false; + return true; + } +}; + +static FunctionCFG getGraph(const model::Binary &Binary, + const SortedVector &CFG, + MetaAddress Entry) { + using namespace efa::FunctionEdgeType; + + FunctionCFG Graph(Entry); + for (const BasicBlock &Block : CFG) { + auto *Source = Graph.get(Block.Start); + + for (const auto &Edge : Block.Successors) { + switch (Edge->Type) { + case DirectBranch: + case FakeFunctionCall: + case FakeFunctionReturn: + case Return: + case BrokenReturn: + case IndirectTailCall: + case LongJmp: + case Unreachable: + Source->addSuccessor(Graph.get(Edge->Destination)); + break; + + case FunctionCall: + case IndirectCall: { + auto *CE = cast(Edge.get()); + if (hasAttribute(Binary, *CE, model::FunctionAttribute::NoReturn)) + Source->addSuccessor(Graph.get(MetaAddress::invalid())); + else + Source->addSuccessor(Graph.get(Block.End)); + break; + } + + case Killer: + Source->addSuccessor(Graph.get(MetaAddress::invalid())); + break; + + case Invalid: + case Count: + revng_abort(); + break; + } + } + } + + return Graph; +} + +bool FunctionMetadata::verify(const model::Binary &Binary) const { + return verify(Binary, false); +} + +bool FunctionMetadata::verify(const model::Binary &Binary, bool Assert) const { + model::VerifyHelper VH(Assert); + return verify(Binary, VH); +} + +bool FunctionMetadata::verify(const model::Binary &Binary, + model::VerifyHelper &VH) const { + const auto &Function = Binary.Functions.at(Entry); + + if (Function.Type == model::FunctionType::Fake + || Function.Type == model::FunctionType::Invalid) + return VH.maybeFail(ControlFlowGraph.size() == 0); + + // Populate graph + FunctionCFG Graph = getGraph(Binary, ControlFlowGraph, Entry); + + // Ensure all the nodes are reachable from the entry node + if (not Graph.allNodesAreReachable()) + return VH.fail(); + + // Ensure the only node with no successors is invalid + if (not Graph.hasOnlyInvalidExits()) + return VH.fail(); + + // Verify blocks + if (ControlFlowGraph.size() > 0) { + bool HasEntry = false; + for (const BasicBlock &Block : ControlFlowGraph) { + + if (Block.Start == Entry) { + if (HasEntry) + return VH.fail(); + HasEntry = true; + } + + for (const auto &Edge : Block.Successors) + if (not Edge->verify(VH)) + return VH.fail(); + } + + if (not HasEntry) { + return VH.fail("The function CFG does not contain a block starting at " + "the entry point", + *this); + } + } + + // Check function calls + for (const auto &Block : ControlFlowGraph) { + for (const auto &Edge : Block.Successors) { + if (Edge->Type == efa::FunctionEdgeType::FunctionCall) { + // We're in a direct call, get the callee + const auto *Call = dyn_cast(Edge.get()); + + if (not Call->DynamicFunction.empty()) { + // It's a dynamic call + + if (Call->Destination.isValid()) { + return VH.fail("Destination must be invalid for dynamic function " + "calls"); + } + + auto It = Binary.ImportedDynamicFunctions.find(Call->DynamicFunction); + + // If missing, fail + if (It == Binary.ImportedDynamicFunctions.end()) + return VH.fail("Can't find callee \"" + Call->DynamicFunction + + "\""); + } else { + // Regular call + auto It = Binary.Functions.find(Call->Destination); + + // If missing, fail + if (It == Binary.Functions.end()) + return VH.fail("Can't find callee"); + } + } + } + } + + return true; +} + +void FunctionMetadata::dump() const { + serialize(dbg, *this); +} + +void FunctionMetadata::dumpCFG(const model::Binary &Binary) const { + FunctionCFG FuncCFG = getGraph(Binary, ControlFlowGraph, Entry); + raw_os_ostream Stream(dbg); + WriteGraph(Stream, &FuncCFG); +} + +void FunctionEdge::dump() const { + serialize(dbg, *this); +} + +bool FunctionEdge::verify() const { + return verify(false); +} + +bool FunctionEdge::verify(bool Assert) const { + model::VerifyHelper VH(Assert); + return verify(VH); +} + +static bool +verifyFunctionEdge(model::VerifyHelper &VH, const FunctionEdgeBase &E) { + using namespace efa::FunctionEdgeType; + + switch (E.Type) { + case Invalid: + case Count: + return VH.fail(); + + case DirectBranch: + case FakeFunctionCall: + case FakeFunctionReturn: + if (E.Destination.isInvalid()) + return VH.fail(); + break; + case FunctionCall: { + const auto &Call = cast(E); + if (not(E.Destination.isValid() == Call.DynamicFunction.empty())) + return VH.fail(); + } break; + + case IndirectCall: + case Return: + case BrokenReturn: + case IndirectTailCall: + case LongJmp: + case Killer: + case Unreachable: + if (E.Destination.isValid()) + return VH.fail(); + break; + } + + return true; +} + +bool FunctionEdgeBase::verify() const { + return verify(false); +} + +bool FunctionEdgeBase::verify(bool Assert) const { + model::VerifyHelper VH(Assert); + return verify(VH); +} + +bool FunctionEdgeBase::verify(model::VerifyHelper &VH) const { + if (auto *Edge = dyn_cast(this)) + return VH.maybeFail(Edge->verify(VH)); + else if (auto *Edge = dyn_cast(this)) + return VH.maybeFail(Edge->verify(VH)); + else + revng_abort("Invalid FunctionEdgeBase instance"); + + return false; +} + +void FunctionEdgeBase::dump() const { + serialize(dbg, *this); +} + +bool FunctionEdge::verify(model::VerifyHelper &VH) const { + if (auto *Call = dyn_cast(this)) + return VH.maybeFail(Call->verify(VH)); + else + return verifyFunctionEdge(VH, *this); +} + +void CallEdge::dump() const { + serialize(dbg, *this); +} + +bool CallEdge::verify() const { + return verify(false); +} + +bool CallEdge::verify(bool Assert) const { + model::VerifyHelper VH(Assert); + return verify(VH); +} + +bool CallEdge::verify(model::VerifyHelper &VH) const { + if (Type == efa::FunctionEdgeType::FunctionCall) { + // We're in a direct function call (either dynamic or not) + bool IsDynamic = not DynamicFunction.empty(); + bool HasDestination = Destination.isValid(); + if (not HasDestination and not IsDynamic) + return VH.fail("Direct call is missing Destination"); + else if (HasDestination and IsDynamic) + return VH.fail("Dynamic function calls cannot have a valid Destination"); + } + + return VH.maybeFail(verifyFunctionEdge(VH, *this)); +} + +model::Identifier BasicBlock::name() const { + using llvm::Twine; + return model::Identifier(std::string("bb_") + Start.toString()); +} + +void BasicBlock::dump() const { + serialize(dbg, *this); +} + +bool BasicBlock::verify() const { + return verify(false); +} + +bool BasicBlock::verify(bool Assert) const { + model::VerifyHelper VH(Assert); + return verify(VH); +} + +bool BasicBlock::verify(model::VerifyHelper &VH) const { + if (Start.isInvalid() or End.isInvalid()) + return VH.fail(); + + for (auto &Edge : Successors) + if (not Edge->verify(VH)) + return VH.fail(); + + return true; +} + +} // namespace efa + +template<> +struct llvm::DOTGraphTraits : public DefaultDOTGraphTraits { + DOTGraphTraits(bool Simple = false) : DefaultDOTGraphTraits(Simple) {} + + static std::string + getNodeLabel(const efa::FunctionCFGNode *Node, const efa::FunctionCFG *) { + return Node->Start.toString(); + } + + static std::string getNodeAttributes(const efa::FunctionCFGNode *Node, + const efa::FunctionCFG *Graph) { + if (Node->Start == Graph->entry()) { + return "shape=box,peripheries=2"; + } + + return ""; + } +}; diff --git a/lib/FunctionIsolation/EnforceABI.cpp b/lib/FunctionIsolation/EnforceABI.cpp index 2d95b2919..cdc6b83b9 100644 --- a/lib/FunctionIsolation/EnforceABI.cpp +++ b/lib/FunctionIsolation/EnforceABI.cpp @@ -21,9 +21,10 @@ #include "revng/ABI/FunctionType.h" #include "revng/ADT/LazySmallBitVector.h" #include "revng/ADT/SmallMap.h" +#include "revng/EarlyFunctionAnalysis/CallEdge.h" +#include "revng/EarlyFunctionAnalysis/IRHelpers.h" #include "revng/FunctionIsolation/EnforceABI.h" #include "revng/FunctionIsolation/StructInitializers.h" -#include "revng/Model/CallEdge.h" #include "revng/Model/Register.h" #include "revng/Model/Type.h" #include "revng/Pipeline/AllRegistries.h" @@ -88,9 +89,10 @@ private: void handleRegularFunctionCall(CallInst *Call); CallInst *generateCall(IRBuilder<> &Builder, + MetaAddress Entry, FunctionCallee Callee, - const model::BasicBlock &CallSiteBlock, - const model::CallEdge &CallSite); + const efa::BasicBlock &CallSiteBlock, + const efa::CallEdge &CallSite); private: Module &M; @@ -314,11 +316,13 @@ void EnforceABIImpl::handleRegularFunctionCall(CallInst *Call) { // Identify the corresponding call site in the model MetaAddress BasicBlockAddress = GCBI.getJumpTarget(Call->getParent()); - const model::BasicBlock &Block = FunctionModel.CFG.at(BasicBlockAddress); - const model::CallEdge *CallSite = nullptr; + efa::FunctionMetadata FM = *extractFunctionMetadata(CallerFunction).get(); + + const efa::BasicBlock &Block = FM.ControlFlowGraph.at(BasicBlockAddress); + const efa::CallEdge *CallSite = nullptr; for (const auto &Edge : Block.Successors) { - using namespace model::FunctionEdgeType; - CallSite = dyn_cast(Edge.get()); + using namespace efa::FunctionEdgeType; + CallSite = dyn_cast(Edge.get()); if (CallSite != nullptr) break; } @@ -345,7 +349,11 @@ void EnforceABIImpl::handleRegularFunctionCall(CallInst *Call) { // Generate the call IRBuilder<> Builder(Call); - CallInst *NewCall = generateCall(Builder, Callee, Block, *CallSite); + CallInst *NewCall = generateCall(Builder, + FunctionModel.Entry, + Callee, + Block, + *CallSite); NewCall->copyMetadata(*Call); // Set PC to the expected value @@ -365,9 +373,10 @@ toFunctionPointer(IRBuilder<> &B, Value *V, FunctionType *FT) { } CallInst *EnforceABIImpl::generateCall(IRBuilder<> &Builder, + MetaAddress Entry, FunctionCallee Callee, - const model::BasicBlock &CallSiteBlock, - const model::CallEdge &CallSite) { + const efa::BasicBlock &CallSiteBlock, + const efa::CallEdge &CallSite) { using model::NamedTypedRegister; using model::RawFunctionType; using model::TypedRegister; @@ -377,7 +386,10 @@ CallInst *EnforceABIImpl::generateCall(IRBuilder<> &Builder, llvm::SmallVector Arguments; llvm::SmallVector ReturnCSVs; - model::TypePath PrototypePath = getPrototype(Binary, CallSite); + model::TypePath PrototypePath = getPrototype(Binary, + Entry, + CallSiteBlock.Start, + CallSite); auto Prototype = abi::FunctionType::Layout::make(PrototypePath); revng_assert(Prototype.verify()); diff --git a/lib/FunctionIsolation/IsolateFunctions.cpp b/lib/FunctionIsolation/IsolateFunctions.cpp index 1a0e2c10b..1064f1508 100644 --- a/lib/FunctionIsolation/IsolateFunctions.cpp +++ b/lib/FunctionIsolation/IsolateFunctions.cpp @@ -22,11 +22,9 @@ #include "revng/ADT/KeyedObjectTraits.h" #include "revng/ADT/ZipMapIterator.h" #include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" +#include "revng/EarlyFunctionAnalysis/IRHelpers.h" #include "revng/FunctionIsolation/IsolateFunctions.h" #include "revng/Model/Binary.h" -#include "revng/Model/CallEdge.h" -#include "revng/Model/FunctionEdge.h" -#include "revng/Model/FunctionEdgeBase.h" #include "revng/Pipeline/AllRegistries.h" #include "revng/Pipeline/Contract.h" #include "revng/Pipes/Kinds.h" @@ -157,7 +155,7 @@ public: class IsolateFunctionsImpl { private: - using SuccessorsContainer = std::map; + using SuccessorsContainer = std::map; private: Function *RootFunction = nullptr; @@ -191,7 +189,7 @@ private: void isolate(const model::Function &Function); /// Process a basic block from the model - void handleBasicBlock(const model::BasicBlock &Block, + void handleBasicBlock(const efa::BasicBlock &Block, ValueToValueMapTy &OldToNew, FunctionBlocks &ClonedBlocks); @@ -199,19 +197,19 @@ private: /// /// \return a vector of boundary basic blocks std::vector - cloneAndIdentifyBoundaries(const model::BasicBlock &Block, + cloneAndIdentifyBoundaries(const efa::BasicBlock &Block, ValueToValueMapTy &OldToNew, FunctionBlocks &ClonedBlocks); /// Create the code necessary to handle a direct branch in the IR bool handleDirectBoundary(const Boundary &TheBoundary, - const model::BasicBlock &Block, + const efa::BasicBlock &Block, SuccessorsContainer &ExpectedSuccessors, FunctionBlocks &ClonedBlocks); /// Create the code necessary to handle an indirect branch in the IR bool handleIndirectBoundary(const std::vector &Boundaries, - const model::BasicBlock &Block, + const efa::BasicBlock &Block, const SuccessorsContainer &ExpectedSuccessors, bool CallConsumed, FunctionBlocks &ClonedBlocks); @@ -398,8 +396,8 @@ public: operator bool() const { return State; } }; -static bool isDirectEdge(model::FunctionEdgeType::Values Type) { - using namespace model::FunctionEdgeType; +static bool isDirectEdge(efa::FunctionEdgeType::Values Type) { + using namespace efa::FunctionEdgeType; switch (Type) { case IndirectCall: @@ -423,7 +421,7 @@ static bool isDirectEdge(model::FunctionEdgeType::Values Type) { } } -static bool isIndirectEdge(model::FunctionEdgeType::Values Type) { +static bool isIndirectEdge(efa::FunctionEdgeType::Values Type) { return not isDirectEdge(Type); } @@ -451,11 +449,11 @@ void printAddressListComparison(const LeftMap &ExpectedAddresses, } bool IFI::handleIndirectBoundary(const std::vector &Boundaries, - const model::BasicBlock &Block, + const efa::BasicBlock &Block, const SuccessorsContainer &ExpectedSuccessors, bool CallConsumed, FunctionBlocks &ClonedBlocks) { - std::vector RemainingEdges; + std::vector RemainingEdges; for (auto &[Edge, EdgeUsageCount] : ExpectedSuccessors) if (EdgeUsageCount == 0) RemainingEdges.push_back(Edge); @@ -463,9 +461,9 @@ bool IFI::handleIndirectBoundary(const std::vector &Boundaries, // At this point RemainingEdges must either be a series of // `DirectBranch` or a single one of the indirect ones bool NoMore = RemainingEdges.size() == 0; - using namespace model::FunctionEdgeType; + using namespace efa::FunctionEdgeType; - auto IsDirectEdge = [](const model::FunctionEdgeBase *E) { + auto IsDirectEdge = [](const efa::FunctionEdgeBase *E) { return isDirectEdge(E->Type); }; bool AllDirect = allOrNone(RemainingEdges, IsDirectEdge, false); @@ -519,7 +517,7 @@ bool IFI::handleIndirectBoundary(const std::vector &Boundaries, SortedVector ExpectedAddresses; { auto Inserter = ExpectedAddresses.batch_insert(); - for (const model::FunctionEdgeBase *Edge : RemainingEdges) + for (const efa::FunctionEdgeBase *Edge : RemainingEdges) Inserter.insert(Edge->Destination); } @@ -623,7 +621,7 @@ bool IFI::handleIndirectBoundary(const std::vector &Boundaries, /// \return true if this was a call bool IFI::handleDirectBoundary(const Boundary &TheBoundary, - const model::BasicBlock &Block, + const efa::BasicBlock &Block, SuccessorsContainer &ExpectedSuccessors, FunctionBlocks &ClonedBlocks) { BasicBlock *BB = TheBoundary.Block; @@ -636,13 +634,13 @@ bool IFI::handleDirectBoundary(const Boundary &TheBoundary, bool Match = false; switch (Edge->Type) { - case model::FunctionEdgeType::DirectBranch: + case efa::FunctionEdgeType::DirectBranch: if (not TheBoundary.isCall()) Match = true; break; - case model::FunctionEdgeType::FunctionCall: - case model::FunctionEdgeType::FakeFunctionCall: + case efa::FunctionEdgeType::FunctionCall: + case efa::FunctionEdgeType::FakeFunctionCall: if (TheBoundary.isCall()) Match = true; break; @@ -660,8 +658,8 @@ bool IFI::handleDirectBoundary(const Boundary &TheBoundary, if (TheBoundary.isCall()) { IsCall.set(); - if (Edge->Type == model::FunctionEdgeType::FunctionCall) { - auto *Call = cast(Edge); + if (Edge->Type == efa::FunctionEdgeType::FunctionCall) { + auto *Call = cast(Edge); eraseBranch(BB->getTerminator(), TheBoundary.CalleeBlock); createFunctionCall(BB, Edge->Destination, @@ -685,7 +683,7 @@ bool IFI::handleDirectBoundary(const Boundary &TheBoundary, } std::vector -IFI::cloneAndIdentifyBoundaries(const model::BasicBlock &Block, +IFI::cloneAndIdentifyBoundaries(const efa::BasicBlock &Block, ValueToValueMapTy &OldToNew, FunctionBlocks &ClonedBlocks) { MetaAddress Entry = Block.Start; @@ -739,7 +737,7 @@ IFI::cloneAndIdentifyBoundaries(const model::BasicBlock &Block, return Boundaries; } -void IFI::handleBasicBlock(const model::BasicBlock &Block, +void IFI::handleBasicBlock(const efa::BasicBlock &Block, ValueToValueMapTy &OldToNew, FunctionBlocks &ClonedBlocks) { if (TheLogger.isEnabled()) { @@ -761,9 +759,9 @@ void IFI::handleBasicBlock(const model::BasicBlock &Block, ClonedBlocks); // Handle call to dynamic functions - model::CallEdge *Call = nullptr; + efa::CallEdge *Call = nullptr; for (const auto &Edge : Block.Successors) - if ((Call = dyn_cast(Edge.get()))) + if ((Call = dyn_cast(Edge.get()))) break; if (Call != nullptr and not Call->DynamicFunction.empty()) { @@ -846,8 +844,16 @@ void IFI::isolate(const model::Function &Function) { TheLogger << DoLog; LoggerIndent<> Indent(TheLogger); + // The CFG must exist if the type of function is not `Invalid` + auto *Term = OriginalEntry->getTerminator(); + auto *FMMDNode = Term->getMetadata(FunctionMetadataMDName); + revng_assert(FMMDNode && Function.Type != model::FunctionType::Invalid); + + // Extract function metadata + efa::FunctionMetadata FM = *extractFunctionMetadata(OriginalEntry).get(); + // Process each basic block - for (const model::BasicBlock &Block : Function.CFG) + for (const efa::BasicBlock &Block : FM.ControlFlowGraph) handleBasicBlock(Block, OldToNew, ClonedBlocks); // Create a dummy entry branching to real entry @@ -1060,6 +1066,10 @@ void IFI::run() { GCBI.setMetaAddressMetadata(NewFunction, FunctionEntryMDNName, Function.Entry); + + auto *OriginalEntryTerm = GCBI.getBlockAt(Function.Entry)->getTerminator(); + auto *MDNode = OriginalEntryTerm->getMetadata(FunctionMetadataMDName); + NewFunction->setMetadata(FunctionMetadataMDName, MDNode); } std::set IsolatedFunctions; diff --git a/lib/Lift/CodeGenerator.cpp b/lib/Lift/CodeGenerator.cpp index b0c1493c3..503518709 100644 --- a/lib/Lift/CodeGenerator.cpp +++ b/lib/Lift/CodeGenerator.cpp @@ -37,7 +37,6 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" -#include "revng/ABI/DefaultFunctionPrototype.h" #include "revng/ADT/STLExtras.h" #include "revng/FunctionCallIdentification/FunctionCallIdentification.h" #include "revng/FunctionCallIdentification/PruneRetSuccessors.h" diff --git a/lib/Model/Binary.cpp b/lib/Model/Binary.cpp index aa9baa525..dff6f922b 100644 --- a/lib/Model/Binary.cpp +++ b/lib/Model/Binary.cpp @@ -20,104 +20,6 @@ using namespace llvm; namespace model { -struct FunctionCFGNodeData { - FunctionCFGNodeData(MetaAddress Start) : Start(Start) {} - MetaAddress Start; -}; - -using FunctionCFGNode = ForwardNode; - -/// Graph data structure to represent the CFG for verification purposes -struct FunctionCFG : public GenericGraph { -private: - MetaAddress Entry; - std::map Map; - -public: - FunctionCFG(MetaAddress Entry) : Entry(Entry) {} - -public: - MetaAddress entry() const { return Entry; } - FunctionCFGNode *entryNode() const { return Map.at(Entry); } - -public: - FunctionCFGNode *get(MetaAddress MA) { - FunctionCFGNode *Result = nullptr; - auto It = Map.find(MA); - if (It == Map.end()) { - Result = addNode(MA); - Map[MA] = Result; - } else { - Result = It->second; - } - - return Result; - } - - bool allNodesAreReachable() const { - if (Map.size() == 0) - return true; - - // Ensure all the nodes are reachable from the entry node - df_iterator_default_set Visited; - for (auto &Ignore : depth_first_ext(entryNode(), Visited)) - ; - return Visited.size() == size(); - } - - bool hasOnlyInvalidExits() const { - for (auto &[Address, Node] : Map) - if (Address.isValid() and not Node->hasSuccessors()) - return false; - return true; - } -}; - -static FunctionCFG getGraph(const Binary &Binary, const Function &F) { - using namespace FunctionEdgeType; - - FunctionCFG Graph(F.Entry); - for (const BasicBlock &Block : F.CFG) { - auto *Source = Graph.get(Block.Start); - - for (const auto &Edge : Block.Successors) { - switch (Edge->Type) { - case DirectBranch: - case FakeFunctionCall: - case FakeFunctionReturn: - case Return: - case BrokenReturn: - case IndirectTailCall: - case LongJmp: - case Unreachable: - Source->addSuccessor(Graph.get(Edge->Destination)); - break; - - case FunctionCall: - case IndirectCall: { - auto *CE = cast(Edge.get()); - if (hasAttribute(Binary, *CE, model::FunctionAttribute::NoReturn)) - Source->addSuccessor(Graph.get(MetaAddress::invalid())); - else - Source->addSuccessor(Graph.get(Block.End)); - break; - } - - case Killer: - Source->addSuccessor(Graph.get(MetaAddress::invalid())); - break; - - case Invalid: - case Count: - revng_abort(); - break; - } - } - } - - return Graph; -} - model::TypePath Binary::getPrimitiveType(PrimitiveTypeKind::Values V, uint8_t ByteSize) { PrimitiveType Temporary(V, ByteSize); @@ -145,12 +47,6 @@ TypePath Binary::recordNewType(UpcastablePointer &&T) { return getTypePath(It->get()); } -void Binary::dumpCFG(const Function &F) const { - FunctionCFG CFG = getGraph(*this, F); - raw_os_ostream Stream(dbg); - WriteGraph(Stream, &CFG); -} - bool Binary::verifyTypes() const { return verifyTypes(false); } @@ -217,51 +113,6 @@ bool Binary::verify(VerifyHelper &VH) const { if (not CheckCustomName(F.CustomName)) return VH.fail("Duplicate name", F); - - // Populate graph - FunctionCFG Graph = getGraph(*this, F); - - // Ensure all the nodes are reachable from the entry node - if (not Graph.allNodesAreReachable()) - return VH.fail(); - - // Ensure the only node with no successors is invalid - if (not Graph.hasOnlyInvalidExits()) - return VH.fail(); - - // Check function calls - for (const BasicBlock &Block : F.CFG) { - for (const auto &Edge : Block.Successors) { - - if (Edge->Type == model::FunctionEdgeType::FunctionCall) { - // We're in a direct call, get the callee - const auto *Call = dyn_cast(Edge.get()); - - if (not Call->DynamicFunction.empty()) { - // It's a dynamic call - - if (Call->Destination.isValid()) { - return VH.fail("Destination must be invalid for dynamic function " - "calls"); - } - - auto It = ImportedDynamicFunctions.find(Call->DynamicFunction); - - // If missing, fail - if (It == ImportedDynamicFunctions.end()) - return VH.fail("Can't find callee \"" + Call->DynamicFunction - + "\""); - } else { - // Regular call - auto It = Functions.find(Call->Destination); - - // If missing, fail - if (It == Functions.end()) - return VH.fail("Can't find callee"); - } - } - } - } } // Verify DynamicFunctions @@ -430,32 +281,6 @@ bool Function::verify(bool Assert) const { } bool Function::verify(VerifyHelper &VH) const { - if (Type == FunctionType::Fake or Type == FunctionType::Invalid) - return VH.maybeFail(CFG.size() == 0); - - // Verify blocks - if (CFG.size() > 0) { - bool HasEntry = false; - for (const BasicBlock &Block : CFG) { - - if (Block.Start == Entry) { - if (HasEntry) - return VH.fail(); - HasEntry = true; - } - - for (const auto &Edge : Block.Successors) - if (not Edge->verify(VH)) - return VH.fail(); - } - - if (not HasEntry) { - return VH.fail("The function CFG does not contain a block starting at " - "the entry point", - *this); - } - } - if (Prototype.isValid()) { // The function has a prototype if (not Prototype.get()->verify(VH)) @@ -470,6 +295,10 @@ bool Function::verify(VerifyHelper &VH) const { } } + for (auto &CallSitePrototype : CallSitePrototypes) + if (not CallSitePrototype.verify(VH)) + return VH.fail(); + return true; } @@ -508,149 +337,28 @@ bool DynamicFunction::verify(VerifyHelper &VH) const { return true; } -void FunctionEdge::dump() const { +void CallSitePrototype::dump() const { serialize(dbg, *this); } -bool FunctionEdge::verify() const { +bool CallSitePrototype::verify() const { return verify(false); } -bool FunctionEdge::verify(bool Assert) const { +bool CallSitePrototype::verify(bool Assert) const { VerifyHelper VH(Assert); return verify(VH); } -static bool verifyFunctionEdge(VerifyHelper &VH, const FunctionEdgeBase &E) { - using namespace model::FunctionEdgeType; +bool CallSitePrototype::verify(VerifyHelper &VH) const { + // Prototype is present + if (not Prototype.isValid()) + return VH.fail("Invalid prototype", *this); - switch (E.Type) { - case Invalid: - case Count: + // Prototype is valid + if (not Prototype.get()->verify(VH)) return VH.fail(); - case DirectBranch: - case FakeFunctionCall: - case FakeFunctionReturn: - if (E.Destination.isInvalid()) - return VH.fail(); - break; - case FunctionCall: { - const auto &Call = cast(E); - if (not(E.Destination.isValid() == Call.DynamicFunction.empty())) - return VH.fail(); - } break; - - case IndirectCall: - case Return: - case BrokenReturn: - case IndirectTailCall: - case LongJmp: - case Killer: - case Unreachable: - if (E.Destination.isValid()) - return VH.fail(); - break; - } - - return true; -} - -bool FunctionEdgeBase::verify() const { - return verify(false); -} - -bool FunctionEdgeBase::verify(bool Assert) const { - VerifyHelper VH(Assert); - return verify(VH); -} - -bool FunctionEdgeBase::verify(VerifyHelper &VH) const { - if (auto *Edge = dyn_cast(this)) - return VH.maybeFail(Edge->verify(VH)); - else if (auto *Edge = dyn_cast(this)) - return VH.maybeFail(Edge->verify(VH)); - else - revng_abort("Invalid FunctionEdgeBase instance"); - - return false; -} - -void FunctionEdgeBase::dump() const { - serialize(dbg, *this); -} - -bool FunctionEdge::verify(VerifyHelper &VH) const { - if (auto *Call = dyn_cast(this)) - return VH.maybeFail(Call->verify(VH)); - else - return verifyFunctionEdge(VH, *this); -} - -void CallEdge::dump() const { - serialize(dbg, *this); -} - -bool CallEdge::verify() const { - return verify(false); -} - -bool CallEdge::verify(bool Assert) const { - VerifyHelper VH(Assert); - return verify(VH); -} - -bool CallEdge::verify(VerifyHelper &VH) const { - if (Type == model::FunctionEdgeType::FunctionCall) { - // We're in a direct function call (either dynamic or not) - bool IsDynamic = not DynamicFunction.empty(); - bool HasDestination = Destination.isValid(); - if (not HasDestination and not IsDynamic) - return VH.fail("Direct call is missing Destination"); - else if (HasDestination and IsDynamic) - return VH.fail("Dynamic function calls cannot have a valid Destination"); - - bool HasPrototype = Prototype.isValid(); - if (HasPrototype) - return VH.fail("Direct function calls must not have a prototype"); - } else { - // We're in an indirect call site - if (not Prototype.isValid() or not Prototype.get()->verify(VH)) - return VH.fail("Indirect call has must have a valid prototype"); - } - - return VH.maybeFail(verifyFunctionEdge(VH, *this)); -} - -Identifier BasicBlock::name() const { - using llvm::Twine; - if (not CustomName.empty()) - return CustomName; - else - return Identifier(std::string("bb_") + Start.toString()); -} - -void BasicBlock::dump() const { - serialize(dbg, *this); -} - -bool BasicBlock::verify() const { - return verify(false); -} - -bool BasicBlock::verify(bool Assert) const { - VerifyHelper VH(Assert); - return verify(VH); -} - -bool BasicBlock::verify(VerifyHelper &VH) const { - if (Start.isInvalid() or End.isInvalid() or not CustomName.verify(VH)) - return VH.fail(); - - for (auto &Edge : Successors) - if (not Edge->verify(VH)) - return VH.fail(); - return true; } @@ -837,23 +545,3 @@ bool isELFRelocationBaseRelative(model::Architecture::Values Architecture, } // namespace RelocationType } // namespace model - -template<> -struct llvm::DOTGraphTraits - : public DefaultDOTGraphTraits { - DOTGraphTraits(bool Simple = false) : DefaultDOTGraphTraits(Simple) {} - - static std::string - getNodeLabel(const model::FunctionCFGNode *Node, const model::FunctionCFG *) { - return Node->Start.toString(); - } - - static std::string getNodeAttributes(const model::FunctionCFGNode *Node, - const model::FunctionCFG *Graph) { - if (Node->Start == Graph->entry()) { - return "shape=box,peripheries=2"; - } - - return ""; - } -}; diff --git a/lib/Model/CMakeLists.txt b/lib/Model/CMakeLists.txt index 50a37f676..c05f41eac 100644 --- a/lib/Model/CMakeLists.txt +++ b/lib/Model/CMakeLists.txt @@ -36,18 +36,14 @@ target_tuple_tree_generator( "${MODEL_HEADERS_DIR}/ABI.h" "${MODEL_HEADERS_DIR}/Architecture.h" "${MODEL_HEADERS_DIR}/Argument.h" - "${MODEL_HEADERS_DIR}/BasicBlock.h" "${MODEL_HEADERS_DIR}/Binary.h" "${MODEL_HEADERS_DIR}/CABIFunctionType.h" - "${MODEL_HEADERS_DIR}/CallEdge.h" + "${MODEL_HEADERS_DIR}/CallSitePrototype.h" "${MODEL_HEADERS_DIR}/DynamicFunction.h" "${MODEL_HEADERS_DIR}/EnumEntry.h" "${MODEL_HEADERS_DIR}/EnumType.h" "${MODEL_HEADERS_DIR}/Function.h" "${MODEL_HEADERS_DIR}/FunctionAttribute.h" - "${MODEL_HEADERS_DIR}/FunctionEdge.h" - "${MODEL_HEADERS_DIR}/FunctionEdgeBase.h" - "${MODEL_HEADERS_DIR}/FunctionEdgeType.h" "${MODEL_HEADERS_DIR}/FunctionType.h" "${MODEL_HEADERS_DIR}/NamedTypedRegister.h" "${MODEL_HEADERS_DIR}/PrimitiveType.h"