mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
0f7de0c2e0
We used to mark call to noreturn functions as killers, but this is not correct. Note that this is a temporary solution, we need to explicitly handle such situations.
187 lines
4.4 KiB
C++
187 lines
4.4 KiB
C++
/// \file Binary.cpp
|
|
/// \brief
|
|
|
|
//
|
|
// 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/Model/Binary.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace model {
|
|
|
|
struct FunctionCFGNodeData {
|
|
FunctionCFGNodeData(MetaAddress Start) : Start(Start) {}
|
|
MetaAddress Start;
|
|
};
|
|
|
|
using FunctionCFGNode = ForwardNode<FunctionCFGNodeData>;
|
|
|
|
/// Graph data structure to represent the CFG for verification purposes
|
|
struct FunctionCFG : public GenericGraph<FunctionCFGNode> {
|
|
private:
|
|
MetaAddress Entry;
|
|
std::map<MetaAddress, FunctionCFGNode *> 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<FunctionCFGNode *> 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;
|
|
}
|
|
};
|
|
|
|
bool Binary::verify() const {
|
|
for (const Function &F : Functions) {
|
|
|
|
// Verify individual functions
|
|
if (not F.verify())
|
|
return false;
|
|
|
|
// Ensure all the direct function calls target an existing function
|
|
for (const BasicBlock &Block : F.CFG) {
|
|
for (const FunctionEdge &Edge : Block.Successors) {
|
|
if (Edge.Type == FunctionEdgeType::FunctionCall
|
|
and Functions.count(Edge.Destination) == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static FunctionCFG getGraph(const Function &F) {
|
|
using namespace FunctionEdgeType;
|
|
|
|
FunctionCFG Graph(F.Entry);
|
|
for (const BasicBlock &Block : F.CFG) {
|
|
auto *Source = Graph.get(Block.Start);
|
|
|
|
for (const FunctionEdge &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:
|
|
// TODO: this does not handle noreturn function calls
|
|
Source->addSuccessor(Graph.get(Block.End));
|
|
break;
|
|
|
|
case Killer:
|
|
Source->addSuccessor(Graph.get(MetaAddress::invalid()));
|
|
break;
|
|
|
|
case Invalid:
|
|
revng_abort();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Graph;
|
|
}
|
|
|
|
void Function::dumpCFG() const {
|
|
FunctionCFG CFG = getGraph(*this);
|
|
raw_os_ostream Stream(dbg);
|
|
WriteGraph(Stream, &CFG);
|
|
}
|
|
|
|
bool Function::verify() const {
|
|
// Verify blocks
|
|
for (const BasicBlock &Block : CFG)
|
|
for (const FunctionEdge &Edge : Block.Successors)
|
|
if (not Edge.verify())
|
|
return false;
|
|
|
|
// Populate graph
|
|
FunctionCFG Graph = getGraph(*this);
|
|
|
|
// Ensure all the nodes are reachable from the entry node
|
|
if (not Graph.allNodesAreReachable())
|
|
return false;
|
|
|
|
// Ensure the only node with no successors is invalid
|
|
if (not Graph.hasOnlyInvalidExits())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FunctionEdge::verify() const {
|
|
using namespace model::FunctionEdgeType;
|
|
return Destination.isValid() == hasDestination(Type);
|
|
}
|
|
|
|
} // namespace model
|
|
|
|
template<>
|
|
struct llvm::DOTGraphTraits<model::FunctionCFG *>
|
|
: 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 "";
|
|
}
|
|
};
|