mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
a31fd8587d
If we find values to be used outside their scope, we need to always mark them for assignment, so that they have a dedicated variable that is declared in the right scope. To do this we: 1. Export the logic that decides if a value needs a top-scope variable in a public header, to be used by both `VariableScopeAnalysis` and `MarkAssignments`. 2. Add the `HasUsesOutsideBB` reason for assignment markers
1505 lines
52 KiB
C++
1505 lines
52 KiB
C++
//
|
|
// Copyright rev.ng Labs Srl. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/IR/Argument.h"
|
|
#include "llvm/IR/BasicBlock.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/IR/InstrTypes.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/IR/Value.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/YAMLTraits.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/EarlyFunctionAnalysis/IRHelpers.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/Generated/Early/PrimitiveTypeKind.h"
|
|
#include "revng/Model/IRHelpers.h"
|
|
#include "revng/Model/QualifiedType.h"
|
|
#include "revng/Model/Qualifier.h"
|
|
#include "revng/Model/RawFunctionType.h"
|
|
#include "revng/Model/StructType.h"
|
|
#include "revng/Model/Type.h"
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/FunctionTags.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
#include "revng/Support/YAMLTraits.h"
|
|
|
|
#include "revng-c/Backend/DecompileFunction.h"
|
|
#include "revng-c/Backend/VariableScopeAnalysis.h"
|
|
#include "revng-c/InitModelTypes/InitModelTypes.h"
|
|
#include "revng-c/RestructureCFG/ASTNode.h"
|
|
#include "revng-c/RestructureCFG/ASTTree.h"
|
|
#include "revng-c/RestructureCFG/BeautifyGHAST.h"
|
|
#include "revng-c/RestructureCFG/RestructureCFG.h"
|
|
#include "revng-c/Support/DecompilationHelpers.h"
|
|
#include "revng-c/Support/FunctionTags.h"
|
|
#include "revng-c/Support/IRHelpers.h"
|
|
#include "revng-c/Support/ModelHelpers.h"
|
|
#include "revng-c/TypeNames/LLVMTypeNames.h"
|
|
#include "revng-c/TypeNames/ModelTypeNames.h"
|
|
|
|
using llvm::cast;
|
|
using llvm::dyn_cast;
|
|
using llvm::isa;
|
|
|
|
using llvm::BasicBlock;
|
|
using llvm::CallInst;
|
|
using llvm::Instruction;
|
|
using llvm::raw_ostream;
|
|
using llvm::StringRef;
|
|
|
|
using model::Binary;
|
|
using model::CABIFunctionType;
|
|
using model::QualifiedType;
|
|
using model::Qualifier;
|
|
using model::RawFunctionType;
|
|
using model::TypedefType;
|
|
|
|
using StringToken = llvm::SmallString<32>;
|
|
using TokenMapT = std::map<const llvm::Value *, StringToken>;
|
|
using ModelTypesMap = std::map<const llvm::Value *, const model::QualifiedType>;
|
|
using ValueSet = llvm::SmallPtrSet<const llvm::Instruction *, 32>;
|
|
|
|
static constexpr const char *StackFrameVarName = "stack";
|
|
static constexpr const char *StackPtrVarName = "stack_ptr";
|
|
|
|
static Logger<> Log{ "c-backend" };
|
|
static Logger<> VisitLog{ "c-backend-visit-order" };
|
|
static Logger<> InlineLog{ "c-backend-inline" };
|
|
|
|
/// Helper function that also writes the logged string as a comment in the C
|
|
/// file if the corresponding logger is enabled
|
|
static void decompilerLog(llvm::raw_ostream &Out, const llvm::Twine &Expr) {
|
|
revng_log(Log, Expr.str());
|
|
if (InlineLog.isEnabled())
|
|
Out << "/* " << Expr << " */\n";
|
|
}
|
|
|
|
static void debug_function dumpTokenMap(const TokenMapT &TokenMap) {
|
|
llvm::dbgs() << "========== TokenMap ===========\n";
|
|
for (auto [Value, Token] : TokenMap) {
|
|
llvm::dbgs() << "Value: " << dumpToString(Value) << "\n";
|
|
llvm::dbgs() << "Token: " << Token.str() << "\n";
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
llvm::dbgs() << "==============================\n";
|
|
}
|
|
|
|
/// Traverse all nested typedefs inside \a QT, if any, and merge them into the
|
|
/// top type.
|
|
static void flattenTypedefs(QualifiedType &QT) {
|
|
while (auto *TD = dyn_cast<TypedefType>(QT.UnqualifiedType.getConst())) {
|
|
llvm::copy(TD->UnderlyingType.Qualifiers, QT.Qualifiers.begin());
|
|
QT.UnqualifiedType = TD->UnderlyingType.UnqualifiedType;
|
|
}
|
|
}
|
|
|
|
static void keepOnlyPtrAndArrayQualifiers(QualifiedType &QT) {
|
|
llvm::erase_if(QT.Qualifiers, [](model::Qualifier &Q) {
|
|
return not(model::Qualifier::isPointer(Q) or model::Qualifier::isArray(Q));
|
|
});
|
|
}
|
|
|
|
/// Return the string that represents the given binary operator in C
|
|
static const char *getBinOpString(const llvm::BinaryOperator *BinOp) {
|
|
switch (BinOp->getOpcode()) {
|
|
case Instruction::Add:
|
|
return " + ";
|
|
break;
|
|
case Instruction::Sub:
|
|
return " - ";
|
|
break;
|
|
case Instruction::Mul:
|
|
return " * ";
|
|
break;
|
|
case Instruction::SDiv:
|
|
case Instruction::UDiv:
|
|
return " / ";
|
|
break;
|
|
case Instruction::SRem:
|
|
case Instruction::URem:
|
|
return " % ";
|
|
break;
|
|
case Instruction::LShr:
|
|
case Instruction::AShr:
|
|
return " >> ";
|
|
break;
|
|
case Instruction::Shl:
|
|
return " << ";
|
|
break;
|
|
case Instruction::And:
|
|
return " & ";
|
|
break;
|
|
case Instruction::Or:
|
|
return " | ";
|
|
break;
|
|
case Instruction::Xor:
|
|
return " ^ ";
|
|
break;
|
|
default:
|
|
revng_abort("Unknown const Binary operation");
|
|
}
|
|
}
|
|
|
|
/// Return the string that represents the given comparison operator in C
|
|
static const char *getCmpOpString(const llvm::CmpInst::Predicate &Pred) {
|
|
using llvm::CmpInst;
|
|
switch (Pred) {
|
|
case CmpInst::ICMP_EQ: ///< equal
|
|
return " == ";
|
|
break;
|
|
case CmpInst::ICMP_NE: ///< not equal
|
|
return " != ";
|
|
break;
|
|
case CmpInst::ICMP_UGT: ///< unsigned greater than
|
|
case CmpInst::ICMP_SGT: ///< signed greater than
|
|
return " > ";
|
|
break;
|
|
|
|
case CmpInst::ICMP_UGE: ///< unsigned greater or equal
|
|
case CmpInst::ICMP_SGE: ///< signed greater or equal
|
|
return " >= ";
|
|
break;
|
|
|
|
case CmpInst::ICMP_ULT: ///< unsigned less than
|
|
case CmpInst::ICMP_SLT: ///< signed less than
|
|
return " < ";
|
|
break;
|
|
|
|
case CmpInst::ICMP_ULE: ///< unsigned less or equal
|
|
case CmpInst::ICMP_SLE: ///< signed less or equal
|
|
return " <= ";
|
|
break;
|
|
|
|
default:
|
|
revng_abort("Unknown comparison operator");
|
|
}
|
|
}
|
|
|
|
/// Stateful name assignment for local variables.
|
|
class VarNameGenerator {
|
|
private:
|
|
uint64_t CurVarID = 0;
|
|
|
|
StringToken nextFuncPtrName() {
|
|
StringToken FuncName("func_ptr_");
|
|
FuncName += to_string(CurVarID++);
|
|
return FuncName;
|
|
}
|
|
|
|
public:
|
|
StringToken nextVarName() {
|
|
StringToken VarName("var_");
|
|
VarName += to_string(CurVarID++);
|
|
return VarName;
|
|
}
|
|
|
|
StringToken nextVarName(const llvm::Value *V) {
|
|
if (V->getType()->isPointerTy()) {
|
|
const auto *PtrType = llvm::cast<llvm::PointerType>(V->getType());
|
|
const auto *PointedType = PtrType->getElementType();
|
|
if (PointedType->isFunctionTy())
|
|
return nextFuncPtrName();
|
|
}
|
|
|
|
return nextVarName();
|
|
}
|
|
|
|
StringToken nextSwitchStateVar() {
|
|
StringToken StateVar("break_from_loop_");
|
|
StateVar += to_string(CurVarID++);
|
|
return StateVar;
|
|
}
|
|
|
|
StringToken nextStackArgsVar() {
|
|
StringToken StackArgsVar("stack_args_");
|
|
StackArgsVar += to_string(CurVarID++);
|
|
return StackArgsVar;
|
|
}
|
|
|
|
StringToken nextLocalStructName() {
|
|
StringToken LocalStructName("local_struct_");
|
|
LocalStructName += to_string(CurVarID++);
|
|
return LocalStructName;
|
|
}
|
|
};
|
|
|
|
struct CCodeGenerator {
|
|
private:
|
|
/// The model of the binary being analysed
|
|
const Binary &Model;
|
|
/// The LLVM function that is being decompiled
|
|
const llvm::Function &LLVMFunction;
|
|
/// The model function corresponding to LLVMFunction
|
|
const model::Function &ModelFunction;
|
|
/// The model prototype of ModelFunction
|
|
const model::Type &ParentPrototype;
|
|
/// The (combed) control flow AST
|
|
const ASTTree &GHAST;
|
|
|
|
/// Set of values that have a corresponding local variable which should be
|
|
/// declared at the start of the function
|
|
const ValueSet &TopScopeVariables;
|
|
/// A map containing a model type for each LLVM value in the function
|
|
const ModelTypesMap TypeMap;
|
|
|
|
/// Where to output the decompiled C code
|
|
raw_ostream &Out;
|
|
|
|
/// Name of the local variable used to break out of loops from within nested
|
|
/// switches
|
|
llvm::SmallVector<StringToken> SwitchStateVars;
|
|
|
|
private:
|
|
/// Stateful generator for variable names
|
|
VarNameGenerator NameGenerator;
|
|
/// Keep track of which expression is associated to each LLVM value during the
|
|
/// emission of C code
|
|
TokenMapT TokenMap;
|
|
|
|
private:
|
|
/// Name of the local variable used to break out from loops
|
|
StringToken LoopStateVar;
|
|
|
|
private:
|
|
/// During emission, keep track of the values that already have a declaration
|
|
llvm::SmallPtrSet<const llvm::Value *, 8> AlreadyDeclared;
|
|
|
|
private:
|
|
/// Emission of parentheses may change whether the OPRP is enabled or not
|
|
bool IsOperatorPrecedenceResolutionPassEnabled = false;
|
|
|
|
public:
|
|
CCodeGenerator(const Binary &Model,
|
|
const llvm::Function &LLVMFunction,
|
|
const ASTTree &GHAST,
|
|
const ValueSet &TopScopeVariables,
|
|
raw_ostream &Out) :
|
|
Model(Model),
|
|
LLVMFunction(LLVMFunction),
|
|
ModelFunction(*llvmToModelFunction(Model, LLVMFunction)),
|
|
ParentPrototype(*ModelFunction.Prototype.getConst()),
|
|
GHAST(GHAST),
|
|
TopScopeVariables(TopScopeVariables),
|
|
TypeMap(initModelTypes(LLVMFunction,
|
|
&ModelFunction,
|
|
Model,
|
|
/*PointersOnly=*/false)),
|
|
Out(Out),
|
|
SwitchStateVars() {
|
|
|
|
// TODO: don't use a global loop state variable
|
|
LoopStateVar = "loop_state_var";
|
|
|
|
if (LLVMFunction.getMetadata(ExplicitParenthesesMDName))
|
|
IsOperatorPrecedenceResolutionPassEnabled = true;
|
|
}
|
|
|
|
void emitFunction(bool NeedsLocalStateVar);
|
|
|
|
private:
|
|
/// Visit a GHAST node and all its children recursively, emitting BBs
|
|
/// and control flow statements in the process.
|
|
RecursiveCoroutine<void> emitGHASTNode(const ASTNode *Node);
|
|
|
|
/// Recursively build a C string representing the condition contained
|
|
/// in an ExprNode (which might be composed by one or more subexpressions).
|
|
/// Whenever an atomic node is encountered, the associated basic block is
|
|
/// emitted on-the-fly.
|
|
RecursiveCoroutine<StringToken> buildGHASTCondition(const ExprNode *E);
|
|
|
|
/// Serialize a basic block into a series of C statements.
|
|
void emitBasicBlock(const BasicBlock *BB);
|
|
|
|
private:
|
|
/// Emit an assignment for an instruction, if it is marked for assignemnt,
|
|
/// otherwise add a string containing its expression to the TokenMap.
|
|
/// Control-flow instructions are associated to their condition's token, and
|
|
/// are never emitted directly (they are handled during the GHAST visit)
|
|
StringToken buildExpression(const llvm::Instruction &I);
|
|
|
|
/// Assign a string token and a QualifiedType to an Instruction
|
|
/// operand. If the operand's value is an Instruction itself, the
|
|
/// corresponding token must already exist.
|
|
/// \return true if a new token has been generated for the operand
|
|
RecursiveCoroutine<bool> addOperandToken(const llvm::Value *Operand);
|
|
|
|
/// Emit a C-style function call. If the Prototype is null, it will be
|
|
/// reconstructed from the LLVM types.
|
|
StringToken buildFuncCallExpr(const llvm::CallInst *Call,
|
|
const llvm::StringRef FuncName,
|
|
const model::Type *Prototype = nullptr);
|
|
|
|
/// Handle calls to functions that have been artificially injected by
|
|
/// the decompiler pipeline and have a special meaning for decompilation.
|
|
StringToken handleSpecialFunction(const llvm::CallInst *Call);
|
|
|
|
private:
|
|
StringToken addAlwaysParentheses(const llvm::Twine &Expr);
|
|
|
|
StringToken addParentheses(const llvm::Twine &Expr);
|
|
|
|
StringToken buildDerefExpr(const llvm::Twine &Expr);
|
|
|
|
StringToken buildAddressExpr(const llvm::Twine &Expr);
|
|
|
|
/// Return a C string that represents a cast of \a ExprToCast to a given
|
|
/// \a DestType. If no casting is needed between the two expression, the
|
|
/// original expression is returned.
|
|
StringToken buildCastExpr(StringRef ExprToCast,
|
|
const model::QualifiedType &SrcType,
|
|
const model::QualifiedType &DestType);
|
|
|
|
/// Return a string that represents a C assignment:
|
|
///
|
|
/// [<TYPE>] LHSToken = [<CAST>] RHSToken
|
|
///
|
|
/// \note <TYPE> is added if \a WithDeclaration is true (transforms the
|
|
/// assignment into a declaration + assignment string)
|
|
StringToken buildAssignmentExpr(const model::QualifiedType &LHSType,
|
|
const llvm::StringRef &LHSToken,
|
|
const llvm::StringRef &RHSToken,
|
|
bool WithDeclaration);
|
|
};
|
|
|
|
StringToken CCodeGenerator::addAlwaysParentheses(const llvm::Twine &Expr) {
|
|
return StringToken(("(" + Expr + ")").str());
|
|
}
|
|
|
|
StringToken CCodeGenerator::addParentheses(const llvm::Twine &Expr) {
|
|
if (IsOperatorPrecedenceResolutionPassEnabled)
|
|
return StringToken(Expr.str());
|
|
return StringToken(("(" + Expr + ")").str());
|
|
}
|
|
|
|
StringToken CCodeGenerator::buildDerefExpr(const llvm::Twine &Expr) {
|
|
return StringToken(("*" + addParentheses(Expr)).str());
|
|
}
|
|
|
|
StringToken CCodeGenerator::buildAddressExpr(const llvm::Twine &Expr) {
|
|
return StringToken(("&" + addParentheses(Expr)).str());
|
|
}
|
|
|
|
StringToken
|
|
CCodeGenerator::buildCastExpr(StringRef ExprToCast,
|
|
const model::QualifiedType &SrcType,
|
|
const model::QualifiedType &DestType) {
|
|
StringToken Result = ExprToCast;
|
|
if (SrcType == DestType or not SrcType.UnqualifiedType.isValid()
|
|
or not DestType.UnqualifiedType.isValid())
|
|
return Result;
|
|
|
|
revng_assert((SrcType.isScalar() or SrcType.isPointer())
|
|
and (DestType.isScalar() or DestType.isPointer()));
|
|
|
|
Result.assign(addAlwaysParentheses(getTypeName(DestType)));
|
|
Result.append(addParentheses(ExprToCast));
|
|
|
|
return Result;
|
|
}
|
|
|
|
StringToken
|
|
CCodeGenerator::buildAssignmentExpr(const model::QualifiedType &LHSType,
|
|
const llvm::StringRef &LHSToken,
|
|
const llvm::StringRef &RHSToken,
|
|
bool WithDeclaration) {
|
|
|
|
StringToken AssignmentStr;
|
|
|
|
if (WithDeclaration)
|
|
AssignmentStr += getNamedCInstance(LHSType, LHSToken);
|
|
else
|
|
AssignmentStr += LHSToken;
|
|
|
|
AssignmentStr += " = ";
|
|
AssignmentStr += RHSToken;
|
|
|
|
return AssignmentStr;
|
|
}
|
|
|
|
StringToken
|
|
CCodeGenerator::buildFuncCallExpr(const llvm::CallInst *Call,
|
|
const llvm::StringRef FuncName,
|
|
const model::Type *Prototype /*=nullptr*/) {
|
|
StringToken Expression;
|
|
Expression += FuncName;
|
|
|
|
if (Call->getNumArgOperands() == 0) {
|
|
Expression += "()";
|
|
|
|
} else {
|
|
llvm::StringRef Separator = "(";
|
|
|
|
for (const auto &Arg : Call->arg_operands()) {
|
|
Expression += Separator;
|
|
Expression += TokenMap.at(Arg);
|
|
|
|
Separator = ", ";
|
|
}
|
|
Expression += ")";
|
|
}
|
|
|
|
return Expression;
|
|
}
|
|
|
|
RecursiveCoroutine<bool>
|
|
CCodeGenerator::addOperandToken(const llvm::Value *Operand) {
|
|
revng_log(Log, "\tOperand: " << dumpToString(*Operand));
|
|
|
|
// Instructions must be visited in reverse-postorder when filling the
|
|
// TokenMap
|
|
if (isa<llvm::Instruction>(Operand) or isa<llvm::Argument>(Operand)) {
|
|
revng_assert(TokenMap.contains(Operand));
|
|
rc_return false;
|
|
}
|
|
|
|
revng_assert(not Operand->getType()->isVoidTy());
|
|
if (auto *Undef = dyn_cast<llvm::UndefValue>(Operand)) {
|
|
revng_assert(Undef->getType()->isIntOrPtrTy());
|
|
TokenMap[Operand] = "0";
|
|
|
|
// Unfortunately, since the InlineLogger prints out values inside "/*"
|
|
// comments, printing this if we're using such logger would break the C
|
|
// code, since it would introduce unterminated "/*" comments.
|
|
if (not InlineLog.isEnabled())
|
|
TokenMap[Operand] += " /* undef */";
|
|
rc_return true;
|
|
}
|
|
|
|
if (auto *Null = dyn_cast<llvm::ConstantPointerNull>(Operand)) {
|
|
TokenMap[Operand] = "NULL";
|
|
rc_return true;
|
|
}
|
|
|
|
if (auto *Glob = dyn_cast<llvm::GlobalVariable>(Operand)) {
|
|
TokenMap[Operand] = Glob->getNameOrAsOperand();
|
|
rc_return true;
|
|
}
|
|
|
|
if (auto *Const = dyn_cast<llvm::ConstantInt>(Operand)) {
|
|
llvm::APInt Value = Const->getValue();
|
|
if (Value.isIntN(64)) {
|
|
// TODO: Decide how to print constants
|
|
TokenMap[Operand] = to_string(Value.getLimitedValue());
|
|
} else {
|
|
// In C, even if you can have 128-bit variables, you cannot have 128-bit
|
|
// literals, so we need this hack to assign a big constant value to a
|
|
// 128-bit variable.
|
|
llvm::APInt LowBits = Value.getLoBits(64);
|
|
llvm::APInt HighBits = Value.getHiBits(Value.getBitWidth() - 64);
|
|
|
|
// TODO: Decide how to print constants
|
|
StringToken LowBitsString;
|
|
LowBits.toString(LowBitsString,
|
|
/*radix=*/16,
|
|
/*signed=*/false,
|
|
/*formatAsCLiteral=*/true);
|
|
StringToken HighBitsString;
|
|
HighBits.toString(HighBitsString,
|
|
/*radix=*/16,
|
|
/*signed=*/false,
|
|
/*formatAsCLiteral=*/true);
|
|
|
|
auto CompositeConstant = addParentheses(HighBitsString + " << 64") + " | "
|
|
+ LowBitsString;
|
|
TokenMap[Operand] = addAlwaysParentheses(CompositeConstant).str();
|
|
}
|
|
|
|
rc_return true;
|
|
}
|
|
|
|
if (auto *ConstExpr = dyn_cast<llvm::ConstantExpr>(Operand)) {
|
|
// A constant expression might have its own uninitialized constant operands
|
|
for (const llvm::Value *Op : ConstExpr->operand_values())
|
|
rc_recur addOperandToken(Op);
|
|
|
|
switch (ConstExpr->getOpcode()) {
|
|
case Instruction::IntToPtr: {
|
|
const llvm::Value *ConstExprOperand = ConstExpr->getOperand(0);
|
|
revng_assert(ConstExprOperand);
|
|
|
|
const QualifiedType &SrcType = TypeMap.at(ConstExprOperand);
|
|
const QualifiedType &DstType = TypeMap.at(ConstExpr);
|
|
|
|
// IntToPtr has no effect on values that we already know to be pointers
|
|
if (SrcType.isPointer())
|
|
TokenMap[ConstExpr] = TokenMap.at(ConstExprOperand);
|
|
else
|
|
TokenMap[ConstExpr] = buildCastExpr(TokenMap.at(ConstExprOperand),
|
|
SrcType,
|
|
DstType);
|
|
} break;
|
|
|
|
default:
|
|
rc_return false;
|
|
}
|
|
|
|
rc_return true;
|
|
}
|
|
|
|
rc_return false;
|
|
}
|
|
|
|
StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) {
|
|
|
|
auto *CalledFunc = Call->getCalledFunction();
|
|
revng_assert(CalledFunc, "Special functions should all have a name");
|
|
const auto &FuncName = CalledFunc->getName();
|
|
|
|
StringToken Expression;
|
|
|
|
if (FunctionTags::ModelGEP.isTagOf(CalledFunc)) {
|
|
revng_assert(Call->getNumArgOperands() >= 2);
|
|
|
|
// First argument is a string containing the base type
|
|
auto *CurArg = Call->arg_begin();
|
|
StringRef BaseTypeString = extractFromConstantStringPtr(CurArg->get());
|
|
|
|
QualifiedType CurType = parseQualifiedType(BaseTypeString, Model);
|
|
QualifiedType CurTypePtr = CurType;
|
|
addPointerQualifier(CurTypePtr, Model);
|
|
|
|
// Second argument is the base llvm::Value
|
|
++CurArg;
|
|
|
|
llvm::Value *BaseValue = CurArg->get();
|
|
Expression = TokenMap.at(BaseValue);
|
|
|
|
++CurArg;
|
|
if (CurArg == Call->arg_end()) {
|
|
// If there are no further arguments, we are casting from one reference
|
|
// type to another
|
|
Expression = buildDerefExpr(Expression);
|
|
|
|
} else {
|
|
// The base type is implicitly a pointer, so the first dereference should
|
|
// use "->"
|
|
StringToken CurExpr = addParentheses(Expression);
|
|
StringRef DerefSymbol = "->";
|
|
|
|
// Traverse the model to decide when to emit ".","->" or "[]"
|
|
for (; CurArg != Call->arg_end(); ++CurArg) {
|
|
flattenTypedefs(CurType);
|
|
keepOnlyPtrAndArrayQualifiers(CurType);
|
|
|
|
model::Qualifier *MainQualifier = nullptr;
|
|
if (CurType.Qualifiers.size() > 0)
|
|
MainQualifier = &CurType.Qualifiers.back();
|
|
|
|
// If it's an array, add "[]"
|
|
if (MainQualifier and model::Qualifier::isArray(*MainQualifier)) {
|
|
CurExpr += "[";
|
|
StringToken IndexExpr("");
|
|
|
|
if (auto *Const = dyn_cast<llvm::ConstantInt>(CurArg->get())) {
|
|
Const->getValue().toString(IndexExpr,
|
|
/*base=*/10,
|
|
/*signed=*/false);
|
|
} else {
|
|
IndexExpr = TokenMap.at(CurArg->get());
|
|
}
|
|
|
|
CurExpr += IndexExpr;
|
|
CurExpr += "]";
|
|
// Remove the qualifier we just analysed
|
|
CurType.Qualifiers.pop_back();
|
|
DerefSymbol = ".";
|
|
continue;
|
|
}
|
|
|
|
// We shouldn't be going past pointers in a single ModelGEP
|
|
revng_assert(not MainQualifier);
|
|
CurExpr += DerefSymbol;
|
|
auto *FieldIdxConst = cast<llvm::ConstantInt>(CurArg->get());
|
|
uint64_t FieldIdx = FieldIdxConst->getValue().getLimitedValue();
|
|
|
|
// Find the field name
|
|
const auto *UnqualType = CurType.UnqualifiedType.getConst();
|
|
|
|
if (auto *Struct = dyn_cast<model::StructType>(UnqualType)) {
|
|
CurExpr += Struct->Fields.at(FieldIdx).name();
|
|
CurType = Struct->Fields.at(FieldIdx).Type;
|
|
|
|
} else if (auto *Union = dyn_cast<model::UnionType>(UnqualType)) {
|
|
CurExpr += Union->Fields.at(FieldIdx).name();
|
|
CurType = Union->Fields.at(FieldIdx).Type;
|
|
|
|
} else {
|
|
revng_abort("Unexpected ModelGEP type found: ");
|
|
CurType.dump();
|
|
}
|
|
|
|
DerefSymbol = ".";
|
|
}
|
|
|
|
Expression = CurExpr;
|
|
}
|
|
} else if (FunctionTags::ModelCast.isTagOf(CalledFunc)) {
|
|
// First argument is a string containing the base type
|
|
auto *CurArg = Call->arg_begin();
|
|
StringRef BaseTypeString = extractFromConstantStringPtr(CurArg->get());
|
|
|
|
QualifiedType CurType = parseQualifiedType(BaseTypeString, Model);
|
|
QualifiedType CurTypePtr = CurType;
|
|
|
|
// Second argument is the base llvm::Value
|
|
++CurArg;
|
|
llvm::Value *BaseValue = CurArg->get();
|
|
|
|
// Emit the parenthesized cast expr, and we are done
|
|
StringToken CastExpr = buildCastExpr(TokenMap.at(BaseValue),
|
|
TypeMap.at(BaseValue),
|
|
CurTypePtr);
|
|
Expression = CastExpr;
|
|
} else if (FunctionTags::AddressOf.isTagOf(CalledFunc)) {
|
|
// Second argument is the value being addressed
|
|
const llvm::Value *Arg = Call->getArgOperand(1);
|
|
Expression = buildAddressExpr(TokenMap.at(Arg));
|
|
} else if (FunctionTags::Parentheses.isTagOf(CalledFunc)) {
|
|
Expression = addAlwaysParentheses(TokenMap.at(Call->getArgOperand(0)));
|
|
} else if (FunctionTags::AssignmentMarker.isTagOf(CalledFunc)) {
|
|
const llvm::Value *Arg = Call->getArgOperand(0);
|
|
|
|
// If a local variable has already been declared for the first argument,
|
|
// this marker is transparent
|
|
if (AlreadyDeclared.contains(Arg)) {
|
|
return TokenMap.at(Arg);
|
|
}
|
|
|
|
if (TopScopeVariables.contains(Call)) {
|
|
// If an entry in the TokenMap exists for this value, we have already
|
|
// declared it, hence we just need to emit an assignment it here.
|
|
revng_log(Log, "Already declared! Assigning " << Expression.str());
|
|
Out << buildAssignmentExpr(TypeMap.at(Call),
|
|
TokenMap.at(Call),
|
|
TokenMap.at(Arg),
|
|
/*WithDeclaration=*/false)
|
|
<< ";\n";
|
|
Expression = TokenMap.at(Call);
|
|
} else {
|
|
|
|
revng_log(Log, "\tAssigning " << Expression.str());
|
|
const StringToken VarName = NameGenerator.nextVarName(Call);
|
|
revng_log(Log, "Declaring new local var for " << Expression.str());
|
|
Out << buildAssignmentExpr(TypeMap.at(Call),
|
|
VarName,
|
|
TokenMap.at(Arg),
|
|
/*WithDeclaration=*/true)
|
|
<< ";\n";
|
|
Expression = VarName;
|
|
}
|
|
} else if (FunctionTags::StructInitializer.isTagOf(CalledFunc)) {
|
|
// Struct initializers should be used only to pack together return values
|
|
// of RawFunctionTypes that return multiple values, therefore they must have
|
|
// the same type as the function's return type
|
|
llvm::StructType *StructTy = cast<llvm::StructType>(Call->getType());
|
|
revng_assert(Call->getFunction()->getReturnType() == StructTy);
|
|
|
|
// Get the top function's prototype, which we can use to derive the type of
|
|
// each field that is being initialized.
|
|
auto *RawPrototype = cast<model::RawFunctionType>(&ParentPrototype);
|
|
revng_assert(RawPrototype);
|
|
|
|
// Emit LHS
|
|
StringToken StructTyName = getReturnTypeName(*RawPrototype);
|
|
|
|
// Declare a new variable that contains the struct
|
|
const auto &VarName = NameGenerator.nextVarName();
|
|
Out << StructTyName << " " << VarName << " = ";
|
|
Expression = VarName;
|
|
|
|
// Emit RHS
|
|
char Separator = '{';
|
|
for (const auto &Arg : Call->args()) {
|
|
Out << Separator << " " << TokenMap.at(Arg);
|
|
Separator = ',';
|
|
}
|
|
Out << "};\n";
|
|
} else if (FuncName.startswith("revng_stack_frame")) {
|
|
|
|
// This expression has a pointer type associated to it because it represents
|
|
// a pointer to a local variable, but the variable itself is not a pointer:
|
|
// drop the qualifier when emitting the declaration
|
|
Out << getNamedCInstance(dropPointer(TypeMap.at(Call)), StackFrameVarName)
|
|
<< ";\n";
|
|
Expression = ("&" + StringRef(StackFrameVarName)).str();
|
|
|
|
} else if (FuncName.startswith("revng_call_stack_arguments")) {
|
|
StringToken VarName = NameGenerator.nextStackArgsVar();
|
|
// This expression has a pointer type associated to it, but the actual
|
|
// declaration that is emitted should not have such pointer.
|
|
Out << getNamedCInstance(dropPointer(TypeMap.at(Call)), VarName) << ";\n ";
|
|
Expression = ("&" + StringRef(VarName)).str();
|
|
|
|
} else if (FuncName.startswith("revng_init_local_sp")) {
|
|
// Note: we treat `revng_init_local_sp` separately from other `init`
|
|
// functions so that we can assign a special name to the variable
|
|
// representing the stack pointer.
|
|
|
|
// Print the stack variable declaration
|
|
Out << getNamedCInstance(TypeMap.at(Call), StackPtrVarName) << " = "
|
|
<< addAlwaysParentheses(getNamedCInstance(TypeMap.at(Call), ""))
|
|
<< " revng_init_local_sp();\n";
|
|
|
|
Expression = StackPtrVarName;
|
|
|
|
} else if (FunctionTags::QEMU.isTagOf(CalledFunc)
|
|
or FunctionTags::Helper.isTagOf(CalledFunc)
|
|
or FuncName.startswith("llvm.") or FuncName.startswith("init_")) {
|
|
|
|
Expression = buildFuncCallExpr(Call,
|
|
model::Identifier::fromString(FuncName),
|
|
/*prototype=*/nullptr);
|
|
|
|
// If this call returns an aggregate type, we have to serialize the call
|
|
// immediately. This is needed because the name of the type returned by this
|
|
// function is not in the model: its name is derived from the called
|
|
// function. If we wait for `AssignmentMarker` to emit a declaration for
|
|
// it, we will loose information on which is the type of the returned
|
|
// struct.
|
|
if (Call->getType()->isAggregateType()) {
|
|
StringToken VarName = NameGenerator.nextVarName(Call);
|
|
Out << getReturnType(Call->getCalledFunction()) << " " << VarName << " = "
|
|
<< Expression << ";\n";
|
|
|
|
AlreadyDeclared.insert(Call);
|
|
Expression = VarName;
|
|
}
|
|
} else {
|
|
revng_abort("Unknown non-isolated function");
|
|
}
|
|
|
|
return Expression;
|
|
}
|
|
|
|
StringToken CCodeGenerator::buildExpression(const llvm::Instruction &I) {
|
|
|
|
// Constants, ConstExprs and Globals might not have an associated token yet
|
|
for (const llvm::Value *Operand : I.operand_values()) {
|
|
if (not TokenMap.contains(Operand)) {
|
|
bool Added = addOperandToken(Operand);
|
|
if (Added) {
|
|
decompilerLog(Out, "Added token: " + TokenMap.at(Operand));
|
|
}
|
|
}
|
|
}
|
|
|
|
StringToken Expression;
|
|
|
|
if (auto *Call = dyn_cast<llvm::CallInst>(&I)) {
|
|
if (FunctionTags::CallToLifted.isTagOf(Call)) {
|
|
revng_log(Log, "Emitting call to isolated function");
|
|
|
|
// Retrieve the CallEdge
|
|
const auto &[CallEdge, _] = getCallEdge(Model, Call);
|
|
revng_assert(CallEdge);
|
|
const auto &PrototypePath = getCallSitePrototype(Model, Call);
|
|
|
|
// Construct the callee token (can be a function name or a function
|
|
// pointer)
|
|
StringToken CalleeToken;
|
|
|
|
if (not isa<llvm::Function>(Call->getCalledOperand())) {
|
|
// Indirect Call
|
|
llvm::Value *CalledVal = Call->getCalledOperand();
|
|
revng_assert(CalledVal);
|
|
addOperandToken(CalledVal);
|
|
auto &VarName = TokenMap.at(CalledVal);
|
|
|
|
CalleeToken = addParentheses(VarName);
|
|
|
|
} else {
|
|
if (not CallEdge->DynamicFunction.empty()) {
|
|
// Dynamic Function
|
|
auto &DynFuncID = CallEdge->DynamicFunction;
|
|
auto &DynamicFunc = Model.ImportedDynamicFunctions.at(DynFuncID);
|
|
CalleeToken = DynamicFunc.name();
|
|
|
|
} else {
|
|
// Isolated function
|
|
llvm::Function *CalledFunc = Call->getCalledFunction();
|
|
revng_assert(CalledFunc);
|
|
const model::Function *ModelFunc = llvmToModelFunction(Model,
|
|
*CalledFunc);
|
|
revng_assert(ModelFunc);
|
|
CalleeToken = ModelFunc->name();
|
|
}
|
|
}
|
|
|
|
// Build the call expression
|
|
revng_assert(not CalleeToken.empty());
|
|
auto *Prototype = PrototypePath.get();
|
|
Expression = buildFuncCallExpr(Call, CalleeToken, Prototype);
|
|
|
|
if (auto *RawPrototype = dyn_cast<RawFunctionType>(Prototype)) {
|
|
// RawFunctionTypes are allowed to return more than one value. In this
|
|
// case, we need to derive the name of the struct that will hold the
|
|
// returned values from the callee function, and emit the call
|
|
// immediately.
|
|
// If we were to postpone the emission to the next
|
|
// `AssignmentMarker`, we would loose information on the name of the
|
|
// return struct.
|
|
if (Call->getType()->isAggregateType()) {
|
|
StringToken VarName = NameGenerator.nextVarName(Call);
|
|
Out << getReturnTypeName(*RawPrototype) << " " << VarName << " = "
|
|
<< Expression << ";\n";
|
|
|
|
AlreadyDeclared.insert(Call);
|
|
Expression = VarName;
|
|
}
|
|
} else if (auto *CPrototype = dyn_cast<CABIFunctionType>(Prototype)) {
|
|
// CABIFunctionTypes are allowed to return arrays, which get enclosed in
|
|
// a wrapper whose type name is derive by the callee. If we were to
|
|
// postpone the emission to the next `AssignmentMarker`, we would
|
|
// loose information on the name of the return struct.
|
|
if (CPrototype->ReturnType.isArray()) {
|
|
StringToken VarName = NameGenerator.nextVarName(Call);
|
|
Out << getReturnTypeName(*CPrototype) << " " << VarName << " = "
|
|
<< Expression << ";\n";
|
|
|
|
AlreadyDeclared.insert(Call);
|
|
Expression = VarName;
|
|
}
|
|
} else {
|
|
revng_abort("The called function has an unknown prototype type.");
|
|
}
|
|
} else {
|
|
// Non-lifted function calls have to be dealt with separately
|
|
Expression = handleSpecialFunction(Call);
|
|
}
|
|
|
|
} else if (auto *Load = dyn_cast<llvm::LoadInst>(&I)) {
|
|
const llvm::Value *LoadedArg = Load->getPointerOperand();
|
|
// The pointer operand's type and the actual loaded value's type might have
|
|
// a mismatch. In this case, we want to cast the pointer operand to correct
|
|
// type pointer before dereferencing it.
|
|
QualifiedType ResultPtrType = TypeMap.at(Load);
|
|
addPointerQualifier(ResultPtrType, Model);
|
|
|
|
Expression = (buildDerefExpr(buildCastExpr(TokenMap.at(LoadedArg),
|
|
TypeMap.at(LoadedArg),
|
|
ResultPtrType)))
|
|
.str();
|
|
|
|
} else if (auto *Store = dyn_cast<llvm::StoreInst>(&I)) {
|
|
|
|
const llvm::Value *PointerOp = Store->getPointerOperand();
|
|
const llvm::Value *ValueOp = Store->getValueOperand();
|
|
const QualifiedType &StoredType = TypeMap.at(ValueOp);
|
|
|
|
StringToken PointerOperandExpr = TokenMap.at(PointerOp);
|
|
|
|
Expression = buildAssignmentExpr(StoredType,
|
|
buildDerefExpr(PointerOperandExpr),
|
|
TokenMap.at(ValueOp),
|
|
/*WithDeclaration=*/false);
|
|
|
|
} else if (auto *Select = dyn_cast<llvm::SelectInst>(&I)) {
|
|
|
|
StringToken Condition = TokenMap.at(Select->getCondition());
|
|
const llvm::Value *Op1 = Select->getOperand(1);
|
|
const llvm::Value *Op2 = Select->getOperand(2);
|
|
|
|
StringToken Op1Token = buildCastExpr(TokenMap.at(Op1),
|
|
TypeMap.at(Op1),
|
|
TypeMap.at(Select));
|
|
StringToken Op2Token = buildCastExpr(TokenMap.at(Op2),
|
|
TypeMap.at(Op2),
|
|
TypeMap.at(Select));
|
|
|
|
Expression = (addParentheses(Condition) + " ? " + addParentheses(Op1Token)
|
|
+ " : " + addParentheses(Op2Token))
|
|
.str();
|
|
|
|
} else if (auto *Alloca = dyn_cast<llvm::AllocaInst>(&I)) {
|
|
|
|
// In LLVM IR, an alloca instruction returns a pointer, so the model type
|
|
// associated to this value is actually a pointer to the model type of
|
|
// the variable being allocated. Hence, to get the actual type of the
|
|
// allocated variable, we must drop the pointer qualifier.
|
|
const auto &AllocaType = TypeMap.at(Alloca);
|
|
revng_assert(AllocaType.isPointer());
|
|
const QualifiedType VarType = dropPointer(AllocaType);
|
|
|
|
// Declare a local variable
|
|
const StringToken VarName = NameGenerator.nextVarName(&I);
|
|
Out << getNamedCInstance(VarType, VarName) << ";\n";
|
|
// Use the address of this variable as the token associated to the alloca
|
|
Expression = ("&" + VarName).str();
|
|
|
|
} else if (auto *Ret = dyn_cast<llvm::ReturnInst>(&I)) {
|
|
Expression = "return";
|
|
|
|
if (llvm::Value *ReturnedVal = Ret->getReturnValue())
|
|
Expression += (" " + TokenMap.at(ReturnedVal)).str();
|
|
|
|
} else if (auto *Branch = dyn_cast<llvm::BranchInst>(&I)) {
|
|
// This is never emitted directly in the BB: it is used when
|
|
// emitting control-flow statements during the GHAST visit.
|
|
} else if (auto *Switch = dyn_cast<llvm::SwitchInst>(&I)) {
|
|
// This is never emitted directly in the BB: it is used when emitting
|
|
// control-flow statements during the GHAST visit
|
|
} else if (isa<llvm::IntToPtrInst>(&I) or isa<llvm::PtrToIntInst>(&I)) {
|
|
// Pointer <-> Integer casts are transparent, since the distinction between
|
|
// integers and pointers is left to the model to decide
|
|
const llvm::Value *Operand = I.getOperand(0);
|
|
Expression = TokenMap.at(Operand);
|
|
|
|
} else if (auto *Bin = dyn_cast<llvm::BinaryOperator>(&I)) {
|
|
const llvm::Value *Op1 = Bin->getOperand(0);
|
|
const llvm::Value *Op2 = Bin->getOperand(1);
|
|
const QualifiedType &ResultType = TypeMap.at(Bin);
|
|
|
|
const auto &Op1Token = buildCastExpr(TokenMap.at(Op1),
|
|
TypeMap.at(Op1),
|
|
ResultType);
|
|
const auto &Op2Token = buildCastExpr(TokenMap.at(Op2),
|
|
TypeMap.at(Op2),
|
|
ResultType);
|
|
|
|
// TODO: Integer promotion
|
|
Expression = (addParentheses(Op1Token) + getBinOpString(Bin)
|
|
+ addParentheses(Op2Token))
|
|
.str();
|
|
|
|
} else if (auto *Cmp = dyn_cast<llvm::CmpInst>(&I)) {
|
|
const llvm::Value *Op1 = Cmp->getOperand(0);
|
|
const llvm::Value *Op2 = Cmp->getOperand(1);
|
|
const QualifiedType &ResultType = llvmIntToModelType(Op1->getType(), Model);
|
|
|
|
const auto &Op1Token = buildCastExpr(TokenMap.at(Op1),
|
|
TypeMap.at(Op1),
|
|
ResultType);
|
|
const auto &Op2Token = buildCastExpr(TokenMap.at(Op2),
|
|
TypeMap.at(Op2),
|
|
ResultType);
|
|
|
|
// TODO: Integer promotion
|
|
Expression = (addParentheses(Op1Token) + getCmpOpString(Cmp->getPredicate())
|
|
+ addParentheses(Op2Token))
|
|
.str();
|
|
|
|
} else if (auto *Cast = dyn_cast<llvm::CastInst>(&I)) {
|
|
|
|
const llvm::Value *Op = Cast->getOperand(0);
|
|
Expression = buildCastExpr(TokenMap.at(Op),
|
|
TypeMap.at(Op),
|
|
TypeMap.at(Cast));
|
|
|
|
} else if (auto *ExtractVal = dyn_cast<llvm::ExtractValueInst>(&I)) {
|
|
|
|
// Note: ExtractValues at this point should have been already
|
|
// handled when visiting the instruction that generated their
|
|
// struct operand
|
|
// revng_assert(TokenMap.contains(&I));
|
|
revng_assert(ExtractVal->getNumIndices() == 1);
|
|
|
|
const auto &Idx = ExtractVal->getIndices().back();
|
|
const llvm::Value *AggregateOp = ExtractVal->getAggregateOperand();
|
|
const auto *AggregateType = cast<llvm::StructType>(AggregateOp->getType());
|
|
|
|
Expression = (TokenMap.at(AggregateOp) + "."
|
|
+ getFieldName(AggregateType, Idx).FieldName)
|
|
.str();
|
|
|
|
} else if (auto *Unreach = dyn_cast<llvm::UnreachableInst>(&I)) {
|
|
Expression = "__builtin_unreachable()";
|
|
|
|
} else {
|
|
revng_abort("Unexpected instruction found when decompiling");
|
|
}
|
|
|
|
return Expression;
|
|
}
|
|
|
|
void CCodeGenerator::emitBasicBlock(const llvm::BasicBlock *BB) {
|
|
LoggerIndent Indent{ VisitLog };
|
|
revng_log(VisitLog, "|__ Visiting BB " << BB->getName());
|
|
LoggerIndent MoreIndent{ VisitLog };
|
|
revng_log(Log, "--------- BB " << BB->getName());
|
|
|
|
for (const Instruction &I : *BB) {
|
|
// Guard this checking logger to prevent computing dumpToString if loggers
|
|
// are not enabled.
|
|
if (Log.isEnabled() or InlineLog.isEnabled()) {
|
|
revng_log(Log, "+++++++++");
|
|
decompilerLog(Out, "Analyzing: " + dumpToString(I));
|
|
}
|
|
|
|
StringToken Expression = buildExpression(I);
|
|
|
|
if (not Expression.empty()) {
|
|
if (I.getType()->isVoidTy()) {
|
|
decompilerLog(Out, "Void instruction found: serializing expression");
|
|
Out << Expression << ";\n";
|
|
} else {
|
|
decompilerLog(Out,
|
|
"Adding expression to the TokenMap " + Expression.str());
|
|
TokenMap[&I] = Expression;
|
|
}
|
|
} else {
|
|
decompilerLog(Out, "Nothing to serialize");
|
|
}
|
|
}
|
|
}
|
|
|
|
RecursiveCoroutine<StringToken>
|
|
CCodeGenerator::buildGHASTCondition(const ExprNode *E) {
|
|
|
|
LoggerIndent Indent{ VisitLog };
|
|
revng_log(VisitLog, "|__ Visiting Condition " << E);
|
|
LoggerIndent MoreIndent{ VisitLog };
|
|
|
|
using NodeKind = ExprNode::NodeKind;
|
|
switch (E->getKind()) {
|
|
|
|
case NodeKind::NK_Atomic: {
|
|
revng_log(VisitLog, "(atomic)");
|
|
|
|
// An atomic node holds a reference to the Basic Block that contains the
|
|
// condition used in the conditional expression. In particular, the
|
|
// condition is the value used in the last expression of the basic
|
|
// block.
|
|
|
|
// First, emit the BB
|
|
const AtomicNode *Atomic = cast<AtomicNode>(E);
|
|
llvm::BasicBlock *BB = Atomic->getConditionalBasicBlock();
|
|
revng_assert(BB);
|
|
emitBasicBlock(BB);
|
|
|
|
// Then, extract the token of the last instruction (must be a
|
|
// conditional branch instruction)
|
|
llvm::Instruction *CondTerminator = BB->getTerminator();
|
|
llvm::BranchInst *Br = cast<llvm::BranchInst>(CondTerminator);
|
|
revng_assert(Br->isConditional());
|
|
rc_return TokenMap.at(Br->getCondition());
|
|
} break;
|
|
|
|
case NodeKind::NK_Not: {
|
|
revng_log(VisitLog, "(not)");
|
|
|
|
const NotNode *N = cast<NotNode>(E);
|
|
ExprNode *Negated = N->getNegatedNode();
|
|
|
|
StringToken Expression;
|
|
Expression = ("!"
|
|
+ addAlwaysParentheses(rc_recur buildGHASTCondition(Negated)))
|
|
.str();
|
|
rc_return Expression;
|
|
} break;
|
|
|
|
case NodeKind::NK_And:
|
|
case NodeKind::NK_Or: {
|
|
revng_log(VisitLog, "(and/or)");
|
|
|
|
const BinaryNode *Binary = cast<BinaryNode>(E);
|
|
|
|
const auto &[Child1, Child2] = Binary->getInternalNodes();
|
|
const auto Child1Token = rc_recur buildGHASTCondition(Child1);
|
|
const auto Child2Token = rc_recur buildGHASTCondition(Child2);
|
|
const llvm::StringRef OpToken = E->getKind() == NodeKind::NK_And ? " && " :
|
|
" || ";
|
|
StringToken Expression = addAlwaysParentheses(Child1Token);
|
|
Expression += OpToken;
|
|
Expression += addAlwaysParentheses(Child2Token);
|
|
rc_return Expression;
|
|
} break;
|
|
|
|
default:
|
|
revng_abort("Unknown ExprNode kind");
|
|
}
|
|
}
|
|
|
|
RecursiveCoroutine<void> CCodeGenerator::emitGHASTNode(const ASTNode *N) {
|
|
if (N == nullptr)
|
|
rc_return;
|
|
|
|
revng_log(VisitLog, "|__ GHAST Node " << N->getID());
|
|
LoggerIndent Indent{ VisitLog };
|
|
|
|
auto Kind = N->getKind();
|
|
switch (Kind) {
|
|
|
|
case ASTNode::NodeKind::NK_Break: {
|
|
revng_log(VisitLog, "(NK_Break)");
|
|
|
|
const BreakNode *Break = llvm::cast<BreakNode>(N);
|
|
if (Break->breaksFromWithinSwitch()) {
|
|
revng_assert(not SwitchStateVars.empty()
|
|
and not SwitchStateVars.back().empty());
|
|
Out << SwitchStateVars.back() << " = true;\n";
|
|
}
|
|
};
|
|
[[fallthrough]];
|
|
|
|
case ASTNode::NodeKind::NK_SwitchBreak: {
|
|
revng_log(VisitLog, "(NK_SwitchBreak)");
|
|
|
|
Out << "break;\n";
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_Continue: {
|
|
revng_log(VisitLog, "(NK_Continue)");
|
|
|
|
const ContinueNode *Continue = cast<ContinueNode>(N);
|
|
|
|
// Print the condition computation code of the if statement.
|
|
if (Continue->hasComputation()) {
|
|
IfNode *ComputationIfNode = Continue->getComputationIfNode();
|
|
buildGHASTCondition(ComputationIfNode->getCondExpr());
|
|
|
|
// Actually print the continue statement only if the continue is not
|
|
// implicit (i.e. it is not the last statement of the loop).
|
|
if (not Continue->isImplicit())
|
|
Out << "continue;\n";
|
|
}
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_Code: {
|
|
revng_log(VisitLog, "(NK_Code)");
|
|
|
|
const CodeNode *Code = cast<CodeNode>(N);
|
|
llvm::BasicBlock *BB = Code->getOriginalBB();
|
|
revng_assert(BB != nullptr);
|
|
emitBasicBlock(BB);
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_If: {
|
|
revng_log(VisitLog, "(NK_If)");
|
|
|
|
const IfNode *If = cast<IfNode>(N);
|
|
const StringToken CondExpr = buildGHASTCondition(If->getCondExpr());
|
|
// "If" expression
|
|
// TODO: possibly cast the CondExpr if it's not convertible to boolean?
|
|
revng_assert(not CondExpr.empty());
|
|
Out << "if (" + CondExpr + ") {\n";
|
|
|
|
// "Then" expression (always emitted)
|
|
if (nullptr == If->getThen()) {
|
|
Out << " // Empty\n";
|
|
} else {
|
|
rc_recur emitGHASTNode(If->getThen());
|
|
}
|
|
Out << "}\n";
|
|
|
|
// "Else" expression (optional)
|
|
if (If->hasElse()) {
|
|
Out << "else {\n";
|
|
rc_recur emitGHASTNode(If->getElse());
|
|
Out << "}\n";
|
|
}
|
|
|
|
break;
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_Scs: {
|
|
revng_log(VisitLog, "(NK_Scs)");
|
|
|
|
const ScsNode *LoopBody = cast<ScsNode>(N);
|
|
|
|
// Calculate the string of the condition
|
|
// TODO: possibly cast the CondExpr if it's not convertible to boolean?
|
|
StringToken CondExpr("true");
|
|
if (LoopBody->isDoWhile() or LoopBody->isWhile()) {
|
|
const IfNode *LoopCondition = LoopBody->getRelatedCondition();
|
|
revng_assert(LoopCondition);
|
|
|
|
// Retrieve the expression of the condition as well as emitting its
|
|
// associated basic block
|
|
CondExpr = buildGHASTCondition(LoopCondition->getCondExpr());
|
|
revng_assert(not CondExpr.empty());
|
|
}
|
|
|
|
if (LoopBody->isDoWhile())
|
|
Out << "do ";
|
|
else
|
|
Out << "while (" + CondExpr + ") ";
|
|
|
|
revng_assert(LoopBody->hasBody());
|
|
Out << "{\n";
|
|
rc_recur emitGHASTNode(LoopBody->getBody());
|
|
Out << "}";
|
|
|
|
if (LoopBody->isDoWhile())
|
|
Out << " while (" + CondExpr + ");\n";
|
|
else
|
|
Out << "\n";
|
|
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_List: {
|
|
revng_log(VisitLog, "(NK_List)");
|
|
|
|
const SequenceNode *Seq = cast<SequenceNode>(N);
|
|
for (const ASTNode *Child : Seq->nodes())
|
|
rc_recur emitGHASTNode(Child);
|
|
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_Switch: {
|
|
revng_log(VisitLog, "(NK_Switch)");
|
|
|
|
const SwitchNode *Switch = cast<SwitchNode>(N);
|
|
|
|
// If needed, print the declaration of the switch state variable, which
|
|
// is used by nested switches inside loops to break out of the loop
|
|
if (Switch->needsStateVariable()) {
|
|
revng_assert(Switch->needsLoopBreakDispatcher());
|
|
SwitchStateVars.push_back(NameGenerator.nextSwitchStateVar());
|
|
Out << "bool " << SwitchStateVars.back() << " = false;\n";
|
|
}
|
|
|
|
// Generate the condition of the switch
|
|
StringToken SwitchVarToken;
|
|
model::QualifiedType SwitchVarType;
|
|
llvm::Value *SwitchVar = Switch->getCondition();
|
|
if (SwitchVar) {
|
|
// If the switch is not weaved we need to print the instructions in
|
|
// the basic block before it.
|
|
if (not Switch->isWeaved()) {
|
|
llvm::BasicBlock *BB = Switch->getOriginalBB();
|
|
revng_assert(BB != nullptr); // This is not a switch dispatcher.
|
|
emitBasicBlock(BB);
|
|
}
|
|
SwitchVarToken = TokenMap.at(SwitchVar);
|
|
SwitchVarType = TypeMap.at(SwitchVar);
|
|
} else {
|
|
revng_assert(Switch->getOriginalBB() == nullptr);
|
|
revng_assert(not LoopStateVar.empty());
|
|
// This switch does not come from an instruction: it's a dispatcher
|
|
// for the loop state variable
|
|
SwitchVarToken = LoopStateVar;
|
|
|
|
// TODO: finer decision on the type of the loop state variable
|
|
using model::PrimitiveTypeKind::Unsigned;
|
|
SwitchVarType.UnqualifiedType = Model.getPrimitiveType(Unsigned, 8);
|
|
}
|
|
revng_assert(not SwitchVarToken.empty());
|
|
|
|
if (not SwitchVarType.is(model::TypeKind::PrimitiveType)) {
|
|
model::QualifiedType BoolTy;
|
|
// TODO: finer decision on how to cast structs used in a switch
|
|
using model::PrimitiveTypeKind::Unsigned;
|
|
BoolTy.UnqualifiedType = Model.getPrimitiveType(Unsigned, 8);
|
|
|
|
SwitchVarToken = buildCastExpr(SwitchVarToken, SwitchVarType, BoolTy);
|
|
}
|
|
|
|
// Generate the switch statement
|
|
Out << "switch (" << SwitchVarToken << ") {\n";
|
|
|
|
// Generate the body of the switch (except for the default)
|
|
for (const auto &[Labels, CaseNode] : Switch->cases_const_range()) {
|
|
revng_assert(not Labels.empty());
|
|
// Generate the case label(s) (multiple case labels might share the
|
|
// same body)
|
|
for (uint64_t CaseVal : Labels) {
|
|
Out << "case ";
|
|
if (SwitchVar) {
|
|
llvm::Type *SwitchVarT = SwitchVar->getType();
|
|
auto *IntType = cast<llvm::IntegerType>(SwitchVarT);
|
|
auto *CaseConst = llvm::ConstantInt::get(IntType, CaseVal);
|
|
// TODO: assigned the signedness based on the signedness of the
|
|
// condition
|
|
CaseConst->getValue().print(Out, false);
|
|
} else {
|
|
Out << CaseVal;
|
|
}
|
|
Out << ":\n";
|
|
}
|
|
Out << "{\n";
|
|
|
|
// Generate the case body
|
|
rc_recur emitGHASTNode(CaseNode);
|
|
|
|
Out << "} break;\n";
|
|
}
|
|
|
|
// Generate the default case if it exists
|
|
if (auto *Default = Switch->getDefault()) {
|
|
Out << "default: {\n";
|
|
rc_recur emitGHASTNode(Default);
|
|
Out << "} break;\n";
|
|
}
|
|
|
|
Out << "}\n";
|
|
|
|
// If the switch needs a loop break dispatcher, reset the associated
|
|
// state variable before emitting the switch statement.
|
|
if (Switch->needsLoopBreakDispatcher()) {
|
|
revng_assert(not SwitchStateVars.empty()
|
|
and not SwitchStateVars.back().empty());
|
|
Out << "if (" << SwitchStateVars.back() << ")\nbreak;\n";
|
|
}
|
|
|
|
// If we're done with a switch that generates a state variable to break out
|
|
// of loops, pop it from the stack.
|
|
if (Switch->needsStateVariable()) {
|
|
revng_assert(Switch->needsLoopBreakDispatcher());
|
|
SwitchStateVars.pop_back();
|
|
}
|
|
|
|
} break;
|
|
|
|
case ASTNode::NodeKind::NK_Set: {
|
|
revng_log(VisitLog, "(NK_Set)");
|
|
|
|
const SetNode *Set = cast<SetNode>(N);
|
|
unsigned StateValue = Set->getStateVariableValue();
|
|
revng_assert(not LoopStateVar.empty());
|
|
|
|
// Print an assignment to the loop state variable. This is an artificial
|
|
// variable introduced by the GHAST to enable executing certain pieces
|
|
// of code based on which control-flow branch was taken. This, for
|
|
// example, can be used to jump to the middle of a loop
|
|
// instead of at the start, without emitting gotos.
|
|
Out << LoopStateVar << " = " << StateValue << ";\n";
|
|
} break;
|
|
}
|
|
|
|
rc_return;
|
|
}
|
|
|
|
void CCodeGenerator::emitFunction(bool NeedsLocalStateVar) {
|
|
|
|
revng_log(Log, "========= Emitting Function " << LLVMFunction.getName());
|
|
revng_log(VisitLog, "========= Function " << LLVMFunction.getName());
|
|
LoggerIndent Indent{ VisitLog };
|
|
|
|
// Create a token for each of the function's arguments
|
|
if (auto *RawPrototype = dyn_cast<model::RawFunctionType>(&ParentPrototype)) {
|
|
|
|
const auto &ModelArgs = RawPrototype->Arguments;
|
|
const auto &LLVMArgs = LLVMFunction.args();
|
|
const auto LLVMArgsNum = LLVMFunction.arg_size();
|
|
|
|
const auto &StackArgType = RawPrototype->StackArgumentsType.UnqualifiedType;
|
|
const auto ArgSize = ModelArgs.size();
|
|
revng_assert(LLVMArgsNum == ArgSize
|
|
or (LLVMArgsNum == ArgSize + 1 and StackArgType.isValid()));
|
|
|
|
// Associate each LLVM argument with its name
|
|
for (const auto &[ModelArg, LLVMArg] :
|
|
llvm::zip_first(ModelArgs, LLVMArgs)) {
|
|
revng_log(Log, "Adding token for: " << dumpToString(LLVMArg));
|
|
TokenMap[&LLVMArg] = ModelArg.name().str();
|
|
}
|
|
|
|
// Add a token for the stack arguments
|
|
if (StackArgType.isValid()) {
|
|
const auto *LLVMArg = LLVMFunction.getArg(LLVMArgsNum - 1);
|
|
revng_log(Log, "Adding token for: " << dumpToString(LLVMArg));
|
|
TokenMap[LLVMArg] = "stack_args";
|
|
}
|
|
} else if (auto *CPrototype = dyn_cast<CABIFunctionType>(&ParentPrototype)) {
|
|
|
|
const auto &ModelArgs = CPrototype->Arguments;
|
|
const auto &LLVMArgs = LLVMFunction.args();
|
|
|
|
revng_assert(LLVMFunction.arg_size() == ModelArgs.size());
|
|
|
|
// Associate each LLVM argument with its name
|
|
for (const auto &[ModelArg, LLVMArg] : llvm::zip(ModelArgs, LLVMArgs)) {
|
|
revng_log(Log, "Adding token for: " << dumpToString(LLVMArg));
|
|
TokenMap[&LLVMArg] = ModelArg.name().str();
|
|
}
|
|
} else {
|
|
revng_abort("Functions can only have RawFunctionType or "
|
|
"CABIFunctionType.");
|
|
}
|
|
|
|
// Print function's prototype
|
|
printFunctionPrototype(ParentPrototype, ModelFunction.name(), Out, Model);
|
|
Out << " {\n";
|
|
|
|
// Emit a declaration for the loop state variable, which is used to
|
|
// redirect control flow inside loops (e.g. if we want to jump in the
|
|
// middle of a loop during a certain iteration)
|
|
if (NeedsLocalStateVar)
|
|
Out << "uint64_t " << LoopStateVar << ";\n";
|
|
|
|
// Declare all variables that have the entire function as a scope
|
|
for (const llvm::Value *VarToDeclare : TopScopeVariables) {
|
|
auto VarName = NameGenerator.nextVarName();
|
|
auto VarType = TypeMap.at(VarToDeclare);
|
|
Out << getNamedCInstance(VarType, VarName) << ";\n";
|
|
TokenMap[VarToDeclare] = VarName;
|
|
}
|
|
|
|
// Recursively print the body of this function
|
|
emitGHASTNode(GHAST.getRoot());
|
|
|
|
Out << "}\n";
|
|
}
|
|
|
|
static std::string decompileFunction(const llvm::Function &LLVMFunc,
|
|
const ASTTree &CombedAST,
|
|
const Binary &Model,
|
|
const ValueSet &TopScopeVariables,
|
|
bool NeedsLocalStateVar) {
|
|
std::string Result;
|
|
|
|
llvm::raw_string_ostream Out(Result);
|
|
CCodeGenerator Backend(Model, LLVMFunc, CombedAST, TopScopeVariables, Out);
|
|
Backend.emitFunction(NeedsLocalStateVar);
|
|
Out.flush();
|
|
|
|
return Result;
|
|
}
|
|
|
|
void decompile(llvm::Module &Module,
|
|
const model::Binary &Model,
|
|
revng::pipes::FunctionStringMap &DecompiledFunctions) {
|
|
|
|
if (Log.isEnabled())
|
|
writeToFile(Model.toString(), "model-during-c-codegen.yaml");
|
|
|
|
for (llvm::Function &F : FunctionTags::Isolated.functions(&Module)) {
|
|
|
|
if (F.empty())
|
|
continue;
|
|
|
|
// TODO: this will eventually become a GHASTContainer for revng pipeline
|
|
ASTTree GHAST;
|
|
|
|
// Generate the GHAST and beautify it.
|
|
{
|
|
restructureCFG(F, GHAST);
|
|
// TODO: beautification should be optional, but at the moment it's not
|
|
// truly so (if disabled, things crash). We should strive to make it
|
|
// optional for real.
|
|
beautifyAST(F, GHAST);
|
|
}
|
|
|
|
if (Log.isEnabled()) {
|
|
const llvm::Twine &ASTFileName = F.getName()
|
|
+ "GHAST-during-c-codegen.dot";
|
|
GHAST.dumpASTOnFile(ASTFileName.str());
|
|
}
|
|
|
|
// Generated C code for F
|
|
auto TopScopeVariables = collectTopScopeVariables(F);
|
|
auto NeedsLoopStateVar = hasLoopDispatchers(GHAST);
|
|
std::string CCode = decompileFunction(F,
|
|
GHAST,
|
|
Model,
|
|
TopScopeVariables,
|
|
NeedsLoopStateVar);
|
|
|
|
// Push the C code into
|
|
MetaAddress Key = getMetaAddressMetadata(&F, "revng.function.entry");
|
|
DecompiledFunctions.insert_or_assign(Key, std::move(CCode));
|
|
}
|
|
}
|