// // Copyright rev.ng Labs Srl. See LICENSE.md for details. // #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.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/IntrinsicInst.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include "revng/ABI/FunctionType/Layout.h" #include "revng/EarlyFunctionAnalysis/FunctionMetadataCache.h" #include "revng/Model/Binary.h" #include "revng/Model/IRHelpers.h" #include "revng/Model/PrimitiveTypeKind.h" #include "revng/Model/QualifiedType.h" #include "revng/Model/Qualifier.h" #include "revng/Model/RawFunctionType.h" #include "revng/Model/Segment.h" #include "revng/Model/StructType.h" #include "revng/Model/Type.h" #include "revng/PTML/Constants.h" #include "revng/PTML/IndentedOstream.h" #include "revng/PTML/ModelHelpers.h" #include "revng/Pipeline/Location.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/InitModelTypes/InitModelTypes.h" #include "revng-c/Pipes/Ranks.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/Support/PTMLC.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 modelEditPath::getCustomNamePath; using pipeline::serializedLocation; using ptml::str; using ptml::Tag; namespace ranks = revng::ranks; namespace attributes = ptml::attributes; namespace tokens = ptml::c::tokens; namespace tags = ptml::tags; using tokenDefinition::types::StringToken; using tokenDefinition::types::TypeString; using TokenMapT = std::map; using ModelTypesMap = std::map; using InstrSetVec = llvm::SmallSetVector; static constexpr const char *StackFrameVarName = "stack"; static Logger<> Log{ "c-backend" }; static Logger<> VisitLog{ "c-backend-visit-order" }; static Logger<> InlineLog{ "c-backend-inline" }; /// Visit the node and all its children recursively, checking if a loop /// variable is needed. // TODO: This could be precomputed and attached to the SCS node in the GHAST. static RecursiveCoroutine needsLoopVar(ASTNode *N) { if (N == nullptr) rc_return false; auto Kind = N->getKind(); switch (Kind) { case ASTNode::NodeKind::NK_Break: case ASTNode::NodeKind::NK_SwitchBreak: case ASTNode::NodeKind::NK_Continue: case ASTNode::NodeKind::NK_Code: rc_return false; break; case ASTNode::NodeKind::NK_If: { IfNode *If = cast(N); if (nullptr != If->getThen()) if (rc_recur needsLoopVar(If->getThen())) rc_return true; if (If->hasElse()) if (rc_recur needsLoopVar(If->getElse())) rc_return true; rc_return false; } break; case ASTNode::NodeKind::NK_Scs: { ScsNode *LoopBody = cast(N); rc_return rc_recur needsLoopVar(LoopBody->getBody()); } break; case ASTNode::NodeKind::NK_List: { SequenceNode *Seq = cast(N); for (ASTNode *Child : Seq->nodes()) if (rc_recur needsLoopVar(Child)) rc_return true; rc_return false; } break; case ASTNode::NodeKind::NK_Switch: { SwitchNode *Switch = cast(N); llvm::Value *SwitchVar = Switch->getCondition(); if (not SwitchVar) rc_return true; for (const auto &[Labels, CaseNode] : Switch->cases()) if (rc_recur needsLoopVar(CaseNode)) rc_return true; if (auto *Default = Switch->getDefault()) if (rc_recur needsLoopVar(Default)) rc_return true; rc_return false; } break; case ASTNode::NodeKind::NK_Set: { rc_return true; } break; } } static bool hasLoopDispatchers(const ASTTree &GHAST) { return needsLoopVar(GHAST.getRoot()); } static InstrSetVec collectTopScopeVariables(const llvm::Function &F) { InstrSetVec TopScopeVars; // We always want to put the stack frame among the top-scope variables, // since it is is logical for it to appear at the top of the function even // if it is used only in a later scope. { bool Found = false; for (const BasicBlock &BB : F) { for (const Instruction &I : BB) { if (isCallTo(&I, "revng_stack_frame")) { revng_assert(not Found); TopScopeVars.insert(&I); Found = true; } } } } for (const BasicBlock &BB : F) { for (const Instruction &I : BB) { // All the others have already been promoted to LocalVariable Copy and // Assign. if (isCallToTagged(&I, FunctionTags::QEMU) or isCallToTagged(&I, FunctionTags::Helper) or isCallToTagged(&I, FunctionTags::Exceptional) or llvm::isa(I)) { const auto *Called = llvm::cast(&I)->getCalledFunction(); revng_assert(not Called->isTargetIntrinsic()); if (needsTopScopeDeclaration(I)) TopScopeVars.insert(&I); } } } return TopScopeVars; } /// 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()) { auto CommentScope = helpers::BlockComment(Out); Out << Expr; } } 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 << "\n"; llvm::dbgs() << "\n"; } llvm::dbgs() << "==============================\n"; } static void debug_function dumpToString(llvm::Value *Value) { llvm::dbgs() << "Value: " << dumpToString(*Value) << "\n"; } static StringToken addAlwaysParentheses(const llvm::Twine &Expr) { return StringToken(("(" + Expr + ")").str()); } static StringToken get128BitIntegerHexConstant(llvm::APInt Value) { revng_assert(Value.getBitWidth() > 64); // 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); StringToken LowBitsString; LowBits.toString(LowBitsString, /*radix=*/16, /*signed=*/false, /*formatAsCLiteral=*/true); StringToken HighBitsString; HighBits.toString(HighBitsString, /*radix=*/16, /*signed=*/false, /*formatAsCLiteral=*/true); auto HighConstant = constants::constant(HighBitsString) + " " + operators::LShift + " " + constants::number(64); auto CompositeConstant = HighConstant + " " + operators::Or + " " + constants::constant(LowBitsString); return addAlwaysParentheses(CompositeConstant).str(); } static StringToken hexLiteral(const llvm::ConstantInt *Int) { StringToken Formatted; if (Int->getBitWidth() <= 64) { Int->getValue().toString(Formatted, /*radix*/ 16, /*signed*/ false, /*formatAsCLiteral*/ true); return Formatted; } return get128BitIntegerHexConstant(Int->getValue()); } static StringToken charLiteral(const llvm::ConstantInt *Int) { revng_assert(Int->getValue().getBitWidth() == 8); const auto LimitedValue = Int->getLimitedValue(0xffu); const auto CharValue = static_cast(LimitedValue); std::string EscapedC; llvm::raw_string_ostream EscapeCStream(EscapedC); EscapeCStream.write_escaped(std::string(&CharValue, 1)); std::string EscapedHTML; llvm::raw_string_ostream EscapeHTMLStream(EscapedHTML); llvm::printHTMLEscaped(EscapedC, EscapeHTMLStream); return StringToken(llvm::formatv("'{0}'", EscapeHTMLStream.str())); } static StringToken boolLiteral(const llvm::ConstantInt *Int) { if (Int->isZero()) { return StringToken("false"); } else { return StringToken("true"); } } /// Return the string that represents the given binary operator in C static const std::string getBinOpString(const llvm::BinaryOperator *BinOp) { const Tag *Op = [&BinOp]() constexpr { switch (BinOp->getOpcode()) { case Instruction::Add: return &operators::Add; case Instruction::Sub: return &operators::Sub; case Instruction::Mul: return &operators::Mul; case Instruction::SDiv: case Instruction::UDiv: return &operators::Div; case Instruction::SRem: case Instruction::URem: return &operators::Modulo; case Instruction::LShr: case Instruction::AShr: return &operators::RShift; case Instruction::Shl: return &operators::LShift; case Instruction::And: return &operators::And; case Instruction::Or: return &operators::Or; case Instruction::Xor: return &operators::Xor; default: revng_abort("Unknown const Binary operation"); } } (); return " " + *Op + " "; } /// Return the string that represents the given comparison operator in C static const std::string getCmpOpString(const llvm::CmpInst::Predicate &Pred) { using llvm::CmpInst; const Tag *Op = [&Pred]() constexpr { switch (Pred) { case CmpInst::ICMP_EQ: ///< equal return &operators::CmpEq; case CmpInst::ICMP_NE: ///< not equal return &operators::CmpNeq; case CmpInst::ICMP_UGT: ///< unsigned greater than case CmpInst::ICMP_SGT: ///< signed greater than return &operators::CmpGt; case CmpInst::ICMP_UGE: ///< unsigned greater or equal case CmpInst::ICMP_SGE: ///< signed greater or equal return &operators::CmpGte; case CmpInst::ICMP_ULT: ///< unsigned less than case CmpInst::ICMP_SLT: ///< signed less than return &operators::CmpLt; case CmpInst::ICMP_ULE: ///< unsigned less or equal case CmpInst::ICMP_SLE: ///< signed less or equal return &operators::CmpLte; default: revng_abort("Unknown comparison operator"); } } (); return " " + *Op + " "; } /// Stateful name assignment for local variables. class VarNameGenerator { private: uint64_t CurVarID = 0; public: StringToken nextVarName() { StringToken VarName("var_"); VarName += to_string(CurVarID++); return VarName; } 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 InstrSetVec &TopScopeVariables; /// A map containing a model type for each LLVM value in the function const ModelTypesMap TypeMap; /// Where to output the decompiled C code ptml::PTMLIndentedOstream Out; /// Name of the local variable used to break out of loops from within nested /// switches std::vector SwitchStateVars; FunctionMetadataCache &Cache; 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, specifically the PTML string representing: /// * In the case of a variable its use, which will have a location reference /// (Indicating that the value of the variable has been used in that /// instance) // clang-format off /// \code{.c} /// int foo = bar; /// //^^^-- marked up with a 'location-reference' for variable bar /// \endcode // clang-format on /// * In the case of an expression the combination of variables from above /// and, /// when needed, the marked-up operators // clang-format off /// \code{.c} /// int baz = foo + bar; /// // ^------ operator '+' marked as such /// //^^^---^^^-- both marked up with the respective variable /// \endcode // clang-format on TokenMapT TokenMap; private: /// Name of the local variable used to break out from loops std::string LoopStateVar; std::string LoopStateVarDeclaration; private: /// Emission of parentheses may change whether the OPRP is enabled or not bool IsOperatorPrecedenceResolutionPassEnabled = false; public: CCodeGenerator(FunctionMetadataCache &Cache, const Binary &Model, const llvm::Function &LLVMFunction, const ASTTree &GHAST, const InstrSetVec &TopScopeVariables, raw_ostream &Out) : Model(Model), LLVMFunction(LLVMFunction), ModelFunction(*llvmToModelFunction(Model, LLVMFunction)), ParentPrototype(*ModelFunction.Prototype().getConst()), GHAST(GHAST), TopScopeVariables(TopScopeVariables), TypeMap(initModelTypes(Cache, LLVMFunction, &ModelFunction, Model, /*PointersOnly=*/false)), Out(Out, 4), SwitchStateVars(), Cache(Cache) { // TODO: don't use a global loop state variable LoopStateVar = getVariableLocationReference("loop_state_var", ModelFunction); LoopStateVarDeclaration = getVariableLocationDefinition("loop_state_var", ModelFunction); 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 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 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 assignment, /// 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 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 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: /// /// [] LHSToken = [] RHSToken /// /// \note is added if \a LHSTokens.Declaration is not empty (transforms /// the assignment into a declaration + assignment string) StringToken buildAssignmentExpr(const model::QualifiedType &LHSType, const VariableTokens &LHSTokens, const llvm::StringRef &RHSToken); private: /// Implements special naming policies for special variables (e.g. stack /// frame) VariableTokens createVarName(const llvm::Value *V) { StringToken VarName; if (isCallTo(V, "revng_stack_frame")) { VarName = StackFrameVarName; } else if (isCallTo(V, "revng_call_stack_argument")) { VarName = NameGenerator.nextStackArgsVar(); } else { VarName = NameGenerator.nextVarName(); } return { getVariableLocationDefinition(VarName.str(), ModelFunction), getVariableLocationReference(VarName.str(), ModelFunction) }; } /// Returns a variable name and a boolean indicating if the variable is new /// (i.e. it has never been declared) or it was already declared. VariableTokens getOrCreateVarName(const llvm::Value *V) { if (const auto *I = dyn_cast(V); I && TopScopeVariables.contains(I)) return { TokenMap.at(I) }; VariableTokens NewVar = createVarName(V); TokenMap[V] = NewVar.Use.str().str(); return NewVar; } }; 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(operators::PointerDereference + addParentheses(Expr)); } StringToken CCodeGenerator::buildAddressExpr(const llvm::Twine &Expr) { return StringToken(operators::AddressOf + addParentheses(Expr)); } 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 VariableTokens &LHSTokens, const llvm::StringRef &RHSToken) { StringToken AssignmentStr; if (LHSTokens.hasDeclaration()) AssignmentStr += getNamedCInstance(LHSType, LHSTokens.Declaration); else AssignmentStr += LHSTokens.Use; AssignmentStr += " " + operators::Assign + " "; 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 CCodeGenerator::addOperandToken(const llvm::Value *Operand) { revng_log(Log, "\tOperand: " << dumpToString(*Operand)); if (Operand->getType()->isAggregateType()) { // Aggregate operands can either be: // 1. constant or global aggregates, which are not currently handled revng_assert(isa(Operand), "The only aggregate operands should be generated by " "special-cased instructions"); // 2. aggregates returned by special-cased call functions, which should // have already added a token for this operand at this point revng_assert(TokenMap.contains(Operand), "Tokens for aggregate operands should have already be " "inserted when visiting the instruction tha returns such " "aggregate."); rc_return false; } // Instructions must be visited in reverse-postorder when filling the // TokenMap if (isa(Operand) or isa(Operand)) { if (auto *CallToCopy = isCallToTagged(Operand, FunctionTags::Copy)) { if (auto *LocalVar = isCallToTagged(CallToCopy->getArgOperand(0), FunctionTags::LocalVariable)) { revng_assert(TokenMap.contains(LocalVar), dumpToString(LocalVar).c_str()); TokenMap[Operand] = TokenMap.at(LocalVar); rc_return true; } } revng_assert(TokenMap.contains(Operand), dumpToString(Operand).c_str()); rc_return false; } revng_assert(not Operand->getType()->isVoidTy()); if (auto *Undef = dyn_cast(Operand)) { revng_assert(Undef->getType()->isIntOrPtrTy()); TokenMap[Operand] = constants::Zero.serialize(); // 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] += " " + helpers::blockComment("undef", false); rc_return true; } if (auto *Poison = dyn_cast(Operand)) { revng_assert(Poison->getType()->isIntOrPtrTy()); TokenMap[Operand] = constants::Zero.serialize(); // 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] += " " + helpers::blockComment("poison", false); rc_return true; } if (auto *Null = dyn_cast(Operand)) { TokenMap[Operand] = constants::Null.serialize(); rc_return true; } if (auto *Glob = dyn_cast(Operand)) { StringRef Name = Glob->getName(); if (Name.size() > 0) { TokenMap[Operand] = Name; rc_return true; } else { rc_return false; } } if (auto *Const = dyn_cast(Operand)) { llvm::APInt Value = Const->getValue(); if (Value.isIntN(64)) { // TODO: Decide how to print constants TokenMap[Operand] = constants::number(Value.getLimitedValue()) .serialize(); } else { TokenMap[Operand] = get128BitIntegerHexConstant(Value).str().str(); } rc_return true; } if (auto *ConstExpr = dyn_cast(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::GetElementPtr: { using namespace llvm; auto *ResultType = ConstExpr->getType(); auto *BasePointer = dyn_cast(ConstExpr->getOperand(0)); auto IsZero = [](llvm::Value *V) { if (auto *CI = dyn_cast(V)) return CI->isZero(); return false; }; if (ResultType->isPointerTy() and ResultType->getPointerElementType()->isIntegerTy(8) and BasePointer != nullptr and BasePointer->isConstant() and BasePointer->hasInitializer() and ConstExpr->getNumOperands() == 3 and IsZero(ConstExpr->getOperand(1)) and IsZero(ConstExpr->getOperand(2))) { // Check if initializer is a CString auto *Initializer = BasePointer->getInitializer(); StringRef Content = ""; if (auto StringInit = dyn_cast(Initializer)) { // If it's not a C string, bail out. if (not StringInit->isCString()) break; // If it's a C string, Drop the terminator. Content = StringInit->getAsString().drop_back(); } else { // Zero initializers are always valid c empty strings, in all the // other cases, bail out if (not isa(Initializer)) break; } std::string Escaped; { raw_string_ostream Stream(Escaped); Stream << "\""; Stream.write_escaped(Content); Stream << "\""; } TokenMap[ConstExpr] = std::move(Escaped); rc_return true; } } break; 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) .str() .str(); rc_return true; } break; } } rc_return false; } /// Traverse all nested typedefs inside \a QT, skipping const Qualifiers, and /// returns a QualifiedType that represents the full traversal. static RecursiveCoroutine flattenTypedefsIgnoringConst(const QualifiedType &QT) { QualifiedType Result = peelConstAndTypedefs(QT); if (auto *TD = dyn_cast(QT.UnqualifiedType().getConst())) { auto &Underlying = TD->UnderlyingType(); QualifiedType Nested = rc_recur flattenTypedefsIgnoringConst(Underlying); Result.UnqualifiedType() = Nested.UnqualifiedType(); llvm::move(Nested.Qualifiers(), std::back_inserter(Result.Qualifiers())); } rc_return Result; } 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) or FunctionTags::ModelGEPRef.isTagOf(CalledFunc)) { revng_assert(Call->getNumArgOperands() >= 2); bool IsRef = FunctionTags::ModelGEPRef.isTagOf(CalledFunc); // First argument is a string containing the base type auto *CurArg = Call->arg_begin(); QualifiedType CurType = deserializeFromLLVMString(CurArg->get(), Model); // Second argument is the base llvm::Value ++CurArg; llvm::Value *BaseValue = CurArg->get(); Expression = TokenMap.at(BaseValue); if (IsRef) { // In ModelGEPRefs, the base value is a reference, and the base type is // its type revng_assert(TypeMap.at(BaseValue) == CurType, "The ModelGEP base type is not coherent with the " "propagated type."); } else { // In ModelGEPs, the base value is a pointer, and the base type is the // type pointed by the base value QualifiedType PointerQt = CurType.getPointerTo(Model.Architecture()); revng_assert(TypeMap.at(BaseValue) == PointerQt, "The ModelGEP base type is not coherent with the " "propagated type."); } // Arguments from the third represent indices for accessing // struct/unions/arrays ++CurArg; if (CurArg == Call->arg_end()) { if (not IsRef) { // If there are no further arguments, we are just dereferencing the // base value Expression = buildDerefExpr(Expression); } else { // Dereferencing a reference does not produce any code } } else { StringToken CurExpr = addParentheses(Expression); Tag DerefSymbol = IsRef ? operators::Dot : operators::Arrow; // Traverse the model to decide whether to emit "." or "[]" for (; CurArg != Call->arg_end(); ++CurArg) { CurType = flattenTypedefsIgnoringConst(CurType); model::Qualifier *MainQualifier = nullptr; if (CurType.Qualifiers().size() > 0) MainQualifier = &CurType.Qualifiers().front(); if (MainQualifier and model::Qualifier::isArray(*MainQualifier)) { // If it's an array, add "[]" std::string IndexExpr; if (auto *Const = dyn_cast(CurArg->get())) { IndexExpr = constants::number(Const->getValue()).serialize(); } else { IndexExpr = TokenMap.at(CurArg->get()); } CurExpr += ("[" + IndexExpr + "]"); // Remove the qualifier we just analysed auto RemainingQualifiers = llvm::drop_begin(CurType.Qualifiers(), 1); CurType.Qualifiers() = { RemainingQualifiers.begin(), RemainingQualifiers.end() }; } else { // We shouldn't be going past pointers in a single ModelGEP revng_assert(not MainQualifier); // If it's a struct or union, we can only navigate it with fixed // indexes. // TODO: decide how to emit constants auto *FieldIdxConst = cast(CurArg->get()); uint64_t FieldIdx = FieldIdxConst->getValue().getLimitedValue(); CurExpr += DerefSymbol.serialize(); // Find the field name const auto *UnqualType = CurType.UnqualifiedType().getConst(); if (auto *Struct = dyn_cast(UnqualType)) { const model::StructField &Field = Struct->Fields().at(FieldIdx); CurExpr += ptml::getLocationReference(*Struct, Field); CurType = Struct->Fields().at(FieldIdx).Type(); } else if (auto *Union = dyn_cast(UnqualType)) { const model::UnionField &Field = Union->Fields().at(FieldIdx); CurExpr += ptml::getLocationReference(*Union, Field); CurType = Union->Fields().at(FieldIdx).Type(); } else { revng_abort("Unexpected ModelGEP type found: "); CurType.dump(); } } // Regardless if the base type was a pointer or not, we are now // navigating only references DerefSymbol = operators::Dot; } Expression = CurExpr; } } else if (FunctionTags::ModelCast.isTagOf(CalledFunc)) { // First argument is a string containing the base type auto *CurArg = Call->arg_begin(); QualifiedType CurType = deserializeFromLLVMString(CurArg->get(), Model); // 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), CurType); Expression = CastExpr; } else if (FunctionTags::AddressOf.isTagOf(CalledFunc)) { // First operand is the type of the value being addressed (should not // introduce casts) QualifiedType ArgType = deserializeFromLLVMString(Call->getArgOperand(0), Model); // Second argument is the value being addressed llvm::Value *Arg = Call->getArgOperand(1); revng_assert(ArgType == TypeMap.at(Arg)); revng_assert(TokenMap.contains(Arg)); Expression = buildAddressExpr(TokenMap.at(Arg)); } else if (FunctionTags::Parentheses.isTagOf(CalledFunc)) { Expression = addAlwaysParentheses(TokenMap.at(Call->getArgOperand(0))); } 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 auto *StructTy = cast(Call->getType()); revng_assert(Call->getFunction()->getReturnType() == StructTy); revng_assert(LLVMFunction.getReturnType() == StructTy); auto StrucTypeName = getNamedInstanceOfReturnType(ParentPrototype, ""); StringToken StructInit = addAlwaysParentheses(StrucTypeName); // Emit RHS llvm::StringRef Separator = "{"; for (const auto &Arg : Call->args()) { StructInit.append(Separator); StructInit.append(" "); StructInit.append(TokenMap.at(Arg)); Separator = ","; } StructInit.append("}\n"); Expression = StructInit; } else if (FunctionTags::LocalVariable.isTagOf(CalledFunc) or FuncName.startswith("revng_call_stack_arguments")) { const auto VarNames = createVarName(Call); // Declare a new local variable if it hasn't already been declared revng_assert(VarNames.hasDeclaration()); Out << getNamedCInstance(TypeMap.at(Call), VarNames.Declaration) << ";\n"; Expression = VarNames.Use; } else if (FuncName.startswith("revng_stack_frame")) { const auto VarNames = getOrCreateVarName(Call); // Declare a new local variable if it hasn't already been declared if (VarNames.hasDeclaration()) { Out << getNamedCInstance(TypeMap.at(Call), VarNames.Declaration) << ";\n"; } Expression = VarNames.Use; } else if (FunctionTags::SegmentRef.isTagOf(CalledFunc)) { const auto &[StartAddress, VirtualSize] = extractSegmentKeyFromMetadata(*CalledFunc); model::Segment Segment = Model.Segments().at({ StartAddress, VirtualSize }); auto Name = Segment.name(); Expression = ptml::getLocationReference(Segment); } else if (FunctionTags::Assign.isTagOf(CalledFunc)) { const llvm::Value *StoredVal = Call->getArgOperand(0); const llvm::Value *PointerVal = Call->getArgOperand(1); const QualifiedType PointedType = TypeMap.at(PointerVal); Expression = buildAssignmentExpr(PointedType, { TokenMap.at(PointerVal) }, TokenMap.at(StoredVal)); } else if (FunctionTags::Copy.isTagOf(CalledFunc)) { // Forward expression Expression = TokenMap.at(Call->getArgOperand(0)); } else if (FunctionTags::OpaqueCSVValue.isTagOf(CalledFunc)) { std::string HelperRef = getHelperFunctionLocationReference(CalledFunc); Expression = buildFuncCallExpr(Call, HelperRef, /*prototype=*/nullptr); } else if (FunctionTags::QEMU.isTagOf(CalledFunc) or FunctionTags::Helper.isTagOf(CalledFunc) or FunctionTags::Exceptional.isTagOf(CalledFunc) or CalledFunc->isIntrinsic()) { revng_assert(not CalledFunc->isTargetIntrinsic()); if (not Call->getType()->isVoidTy()) { const auto VarName = getOrCreateVarName(Call); if (VarName.hasDeclaration()) Out << getReturnTypeLocationReference(CalledFunc) << " " << VarName.Declaration; else Out << VarName.Use; Out << " " << operators::Assign << " "; Expression = VarName.Use; } std::string HelperRef = getHelperFunctionLocationReference(CalledFunc); auto CallExpr = buildFuncCallExpr(Call, HelperRef, /*prototype=*/nullptr); Out << CallExpr << ";\n"; } else if (FunctionTags::HexInteger.isTagOf(CalledFunc)) { const auto Operand = Call->getArgOperand(0); const auto *Value = cast(Operand); Expression = constants::constant(hexLiteral(Value)).serialize(); } else if (FunctionTags::CharInteger.isTagOf(CalledFunc)) { const auto Operand = Call->getArgOperand(0); const auto *Value = cast(Operand); Expression = constants::constant(charLiteral(Value)).serialize(); } else if (FunctionTags::BoolInteger.isTagOf(CalledFunc)) { const auto Operand = Call->getArgOperand(0); const auto *Value = cast(Operand); Expression = constants::constant(boolLiteral(Value)).serialize(); } else if (FunctionTags::UnaryMinus.isTagOf(CalledFunc)) { auto Operand = Call->getArgOperand(0); Expression = operators::UnaryMinus + constants::constant(TokenMap.at(Operand)).serialize(); } else if (FunctionTags::BinaryNot.isTagOf(CalledFunc)) { auto Operand = Call->getArgOperand(0); Expression = (Operand->getType()->isIntegerTy(1) ? operators::BoolNot : operators::BinaryNot) + constants::constant(TokenMap.at(Operand)).serialize(); } else if (FunctionTags::StringLiteral.isTagOf(CalledFunc)) { const auto Operand = Call->getArgOperand(0); const auto Token = StringToken(TokenMap.at(Operand)); Expression = constants::stringLiteral(Token).serialize(); } else { revng_abort("Unknown non-isolated function"); } return Expression; } static bool shouldGenerateDebugInfoAsPTML(const llvm::Instruction &I) { if (!I.getDebugLoc() || !I.getDebugLoc()->getScope()) return false; // If the next instruciton in the BB has different DebugLoc, generate the // PTML location now. auto NextInstr = std::next(I.getIterator()); if (NextInstr == I.getParent()->end() || !NextInstr->getDebugLoc() || NextInstr->getDebugLoc() != I.getDebugLoc()) return true; return false; } 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 FinalExpression; // Emit PTML debug locations, trying to avoid duplications ptml::Tag TheDebugInfoTag(ptml::tags::Span); bool ShouldGenerateDbgInfoPTML = shouldGenerateDebugInfoAsPTML(I); if (ShouldGenerateDbgInfoPTML) { TheDebugInfoTag.addAttribute(ptml::attributes::LocationReferences, I.getDebugLoc()->getScope()->getName()); // Open the tag for debug location. FinalExpression += TheDebugInfoTag.open(); } StringToken Expression; if (auto *Call = dyn_cast(&I)) { if (FunctionTags::CallToLifted.isTagOf(Call)) { revng_log(Log, "Emitting call to isolated function"); // Retrieve the CallEdge const auto &[CallEdge, _] = Cache.getCallEdge(Model, Call); revng_assert(CallEdge); const auto &PrototypePath = Cache.getCallSitePrototype(Model, Call); // Construct the callee token (can be a function name or a function // pointer) StringToken CalleeToken; if (not isa(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); std::string Location = serializedLocation(ranks::DynamicFunction, DynamicFunc.key()); CalleeToken = Tag(tags::Span, DynamicFunc.name().str()) .addAttribute(attributes::Token, tokens::Function) .addAttribute(attributes::ModelEditPath, getCustomNamePath(DynamicFunc)) .addAttribute(attributes::LocationReferences, Location) .serialize(); } else { // Isolated function llvm::Function *CalledFunc = Call->getCalledFunction(); revng_assert(CalledFunc); const model::Function *ModelFunc = llvmToModelFunction(Model, *CalledFunc); revng_assert(ModelFunc); CalleeToken = ModelFunc->name(); CalleeToken = Tag(tags::Span, ModelFunc->name().str()) .addAttribute(attributes::Token, tokens::Function) .addAttribute(attributes::ModelEditPath, getCustomNamePath(*ModelFunc)) .addAttribute(attributes::LocationReferences, serializedLocation(ranks::Function, ModelFunc->key())) .serialize(); } } // Build the call expression revng_assert(not CalleeToken.empty()); auto *Prototype = PrototypePath.get(); Expression = buildFuncCallExpr(Call, CalleeToken, Prototype); const auto Layout = abi::FunctionType::Layout::make(*Prototype); if (Layout.ReturnValues.size() > 1) { revng_assert(Call->getType()->isAggregateType()); // When functions return multiple values, we need to insert a struct // to represent those. // // The name of such a struct should be derived immediately as postponing // the emission to the next `AssignmentMarker` would result in the loss // of the name of this struct. const auto VarNames = createVarName(Call); // Declare a new local variable if it hasn't already been declared if (VarNames.hasDeclaration()) Out << getNamedInstanceOfReturnType(*Prototype, VarNames.Declaration); else Out << VarNames.Use; Out << " " << operators::Assign << " " << Expression << ";\n"; Expression = VarNames.Use; } else if (Layout.ReturnValues.size() == 1) { // Similarly to multiple return values, when returning an array, it // should be enclosed into a struct. if (Layout.ReturnValues.front().Type.isArray()) { revng_assert(llvm::isa(Prototype), "Array return values are only supported when " "the function has `CABIFunctionType`."); const auto VarNames = createVarName(Call); // Declare a new local variable if it hasn't already been declared if (VarNames.hasDeclaration()) Out << getNamedInstanceOfReturnType(*Prototype, VarNames.Declaration); else Out << VarNames.Use; Out << " " << operators::Assign << " " << Expression << ";\n"; Expression = VarNames.Use; } } } else { // Non-lifted function calls have to be dealt with separately Expression = handleSpecialFunction(Call); } } else if (auto *Load = dyn_cast(&I)) { const llvm::Value *Pointer = 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. const model::Architecture::Values &Architecture = Model.Architecture(); QualifiedType ResultPtrType = TypeMap.at(Load).getPointerTo(Architecture); Expression = (buildDerefExpr(buildCastExpr(TokenMap.at(Pointer), TypeMap.at(Pointer), ResultPtrType))) .str(); } else if (auto *Store = dyn_cast(&I)) { const llvm::Value *PointerOp = Store->getPointerOperand(); const llvm::Value *ValueOp = Store->getValueOperand(); const QualifiedType &StoredType = TypeMap.at(ValueOp); const model::Architecture::Values &Architecture = Model.Architecture(); const auto PointerToStoredType = StoredType.getPointerTo(Architecture); StringToken PointerCast = buildCastExpr(TokenMap.at(PointerOp), TypeMap.at(PointerOp), PointerToStoredType); Expression = buildAssignmentExpr(StoredType, { buildDerefExpr(PointerCast) }, TokenMap.at(ValueOp)); } else if (auto *Select = dyn_cast(&I)) { StringToken Condition = StringToken(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 *Ret = dyn_cast(&I)) { Expression = keywords::Return.serialize(); if (llvm::Value *ReturnedVal = Ret->getReturnValue()) Expression += (" " + TokenMap.at(ReturnedVal)); } else if (auto *Branch = dyn_cast(&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(&I)) { // This is never emitted directly in the BB: it is used when emitting // control-flow statements during the GHAST visit } else if (isa(&I) or isa(&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(&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(&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(&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(&I)) { // Note: ExtractValues at this point should have been already // handled when visiting the instruction that generated their // struct operand revng_assert(ExtractVal->getNumIndices() == 1); const auto &Idx = ExtractVal->getIndices().back(); const llvm::Value *AggregateOp = ExtractVal->getAggregateOperand(); const auto *CallReturnsStruct = llvm::cast(AggregateOp); const llvm::Function *Callee = CallReturnsStruct->getCalledFunction(); const auto CalleePrototype = Cache.getCallSitePrototype(Model, CallReturnsStruct); std::string StructFieldRef; if (not CalleePrototype.isValid()) { // The call returning a struct is a call to a helper function. // It must be a direct call. revng_assert(Callee); StructFieldRef = getReturnStructFieldLocationReference(Callee, Idx); } else { const model::Type *CalleeType = CalleePrototype.getConst(); StructFieldRef = getReturnField(*CalleeType, Idx, Model).str().str(); } Expression.assign(TokenMap.at(AggregateOp) + "." + StructFieldRef); } else if (auto *Unreach = dyn_cast(&I)) { Expression = "__builtin_unreachable()"; } else { revng_abort("Unexpected instruction found when decompiling"); } if (Expression.empty()) return Expression; // Clear the TokenMap for operands that only have one use in the same // BasicBlock, and such that I is the last user in the block. // They will be never used after that, and we don't want those strings to hang // around, since we don't want the container to grow too big. for (const llvm::Value *Operand : I.operand_values()) { if (auto *InstructionOp = dyn_cast(Operand)) { bool UserInDifferentBlock = false; for (auto *User : InstructionOp->users()) if (auto *UserInst = cast(User)) if (UserInst->getParent() != InstructionOp->getParent()) UserInDifferentBlock = true; if (not UserInDifferentBlock) { llvm::SmallPtrSet UserInstructions; for (const auto *User : Operand->users()) UserInstructions.insert(cast(User)); bool FoundOtherUser = false; for (const auto &I : llvm::make_range(std::next(I.getIterator()), I.getParent()->end())) if (UserInstructions.contains(&I)) FoundOtherUser = true; if (not FoundOtherUser) TokenMap.erase(Operand); } } } FinalExpression += Expression; // Close the tag for debug location. if (ShouldGenerateDbgInfoPTML) FinalExpression += TheDebugInfoTag.close(); return FinalExpression; } 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.str().str(); } } else { decompilerLog(Out, "Nothing to serialize"); } } } RecursiveCoroutine 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(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(CondTerminator); revng_assert(Br->isConditional()); rc_return StringToken(TokenMap.at(Br->getCondition())); } break; case NodeKind::NK_Not: { revng_log(VisitLog, "(not)"); const NotNode *N = cast(E); ExprNode *Negated = N->getNegatedNode(); StringToken Expression; Expression = operators::BoolNot + addAlwaysParentheses(rc_recur buildGHASTCondition(Negated)); rc_return Expression; } break; case NodeKind::NK_And: case NodeKind::NK_Or: { revng_log(VisitLog, "(and/or)"); const BinaryNode *Binary = cast(E); const auto &[Child1, Child2] = Binary->getInternalNodes(); const auto Child1Token = rc_recur buildGHASTCondition(Child1); const auto Child2Token = rc_recur buildGHASTCondition(Child2); const Tag &OpToken = E->getKind() == NodeKind::NK_And ? operators::BoolAnd : operators::BoolOr; StringToken Expression = addAlwaysParentheses(Child1Token); Expression += " " + OpToken + " "; Expression += addAlwaysParentheses(Child2Token); rc_return Expression; } break; default: revng_abort("Unknown ExprNode kind"); } } RecursiveCoroutine 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(N); if (Break->breaksFromWithinSwitch()) { revng_assert(not SwitchStateVars.empty() and not SwitchStateVars.back().empty()); Out << SwitchStateVars.back() << " " + operators::Assign + " " + constants::True + ";\n"; } }; [[fallthrough]]; case ASTNode::NodeKind::NK_SwitchBreak: { revng_log(VisitLog, "(NK_SwitchBreak)"); Out << keywords::Break << ";\n"; } break; case ASTNode::NodeKind::NK_Continue: { revng_log(VisitLog, "(NK_Continue)"); const ContinueNode *Continue = cast(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 << keywords::Continue << ";\n"; } break; case ASTNode::NodeKind::NK_Code: { revng_log(VisitLog, "(NK_Code)"); const CodeNode *Code = cast(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(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 << keywords::If << " (" + CondExpr + ") "; { Scope TheScope(Out); // "Then" expression (always emitted) if (nullptr == If->getThen()) Out << helpers::lineComment("Empty"); else rc_recur emitGHASTNode(If->getThen()); } // "Else" expression (optional) if (If->hasElse()) { Out << " " + keywords::Else + " "; Scope TheScope(Out); rc_recur emitGHASTNode(If->getElse()); } Out << "\n"; } break; case ASTNode::NodeKind::NK_Scs: { revng_log(VisitLog, "(NK_Scs)"); const ScsNode *LoopBody = cast(N); // Calculate the string of the condition // TODO: possibly cast the CondExpr if it's not convertible to boolean? StringToken CondExpr(constants::True.serialize()); 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 << keywords::Do << " "; else Out << keywords::While + " (" + CondExpr + ") "; revng_assert(LoopBody->hasBody()); { Scope TheScope(Out); rc_recur emitGHASTNode(LoopBody->getBody()); } if (LoopBody->isDoWhile()) Out << " " + keywords::While + " (" + CondExpr + ");"; Out << "\n"; } break; case ASTNode::NodeKind::NK_List: { revng_log(VisitLog, "(NK_List)"); const SequenceNode *Seq = cast(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(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()); StringToken NewVarName = NameGenerator.nextSwitchStateVar(); std::string SwitchStateVar = getVariableLocationReference(NewVarName, ModelFunction); SwitchStateVars.push_back(std::move(SwitchStateVar)); Out << ptml::tokenTag("bool", tokens::Type) << " " << getVariableLocationDefinition(NewVarName, ModelFunction) << " " + operators::Assign + " " + constants::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(!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 << keywords::Switch + " (" << SwitchVarToken << ") "; { Scope TheScope(Out); // 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 << keywords::Case + " "; if (SwitchVar) { llvm::Type *SwitchVarT = SwitchVar->getType(); auto *IntType = cast(SwitchVarT); auto *CaseConst = llvm::ConstantInt::get(IntType, CaseVal); // TODO: assigned the signedness based on the signedness of the // condition Out << constants::number(CaseConst->getValue()); } else { Out << constants::number(CaseVal); } Out << ":\n"; } { Scope InnerScope(Out); // Generate the case body rc_recur emitGHASTNode(CaseNode); } Out << " " + keywords::Break + ";\n"; } // Generate the default case if it exists if (auto *Default = Switch->getDefault()) { Out << keywords::Default << ":\n"; { Scope TheScope(Out); rc_recur emitGHASTNode(Default); } Out << " " + keywords::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 << keywords::If + " (" + SwitchStateVars.back() + ")"; { auto Scope = scopeTags::Scope.scope(Out, true); auto IndentScope = Out.scope(); Out << keywords::Break + ";"; } Out << "\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(N); unsigned StateValue = Set->getStateVariableValue(); revng_assert(!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 << " " << operators::Assign << " " << 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(&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)); std::string ArgIdentifier = model::Identifier::fromString(ModelArg.name()) .str() .str(); TokenMap[&LLVMArg] = getArgumentLocationReference(ArgIdentifier, ModelFunction); } // 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] = getArgumentLocationReference("stack_args", ModelFunction); } } else if (auto *CPrototype = dyn_cast(&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)); std::string ArgIdentifier = model::Identifier::fromString(ModelArg.name()) .str() .str(); TokenMap[&LLVMArg] = getArgumentLocationReference(ArgIdentifier, ModelFunction); } } else { revng_abort("Functions can only have RawFunctionType or " "CABIFunctionType."); } { auto FunctionTagScope = scopeTags::Function.scope(Out); // Print function's prototype printFunctionPrototype(ParentPrototype, ModelFunction, Out, Model, true); Out << " "; { Scope BraceScope(Out, scopeTags::FunctionBody); if (not TopScopeVariables.empty()) { // Declare all variables that have the entire function as a scope decompilerLog(Out, "Top-Scope Declarations"); for (const llvm::Instruction *VarToDeclare : TopScopeVariables) { if (Log.isEnabled() or InlineLog.isEnabled()) { decompilerLog(Out, "VarToDeclare: " + dumpToString(VarToDeclare)); } VariableTokens VarName = createVarName(VarToDeclare); auto VarTypeIt = TypeMap.find(VarToDeclare); if (VarTypeIt != TypeMap.end()) { Out << getNamedCInstance(TypeMap.at(VarToDeclare), VarName.Declaration) << ";\n"; } else { // The only types that are allowed to be missing from the TypeMap // are LLVM aggregates returned by RawFunctionTypes or by helpers auto *Call = llvm::cast(VarToDeclare); if (const auto &Prototype = Cache.getCallSitePrototype(Model, Call); Prototype.isValid() and not Prototype.empty()) { const auto *FunctionType = Prototype.getConst(); Out << getNamedInstanceOfReturnType(*FunctionType, VarName.Declaration) << ";\n"; } else { auto *CalledFunction = Call->getCalledFunction(); revng_assert(CalledFunction); Out << getReturnTypeLocationReference(CalledFunction) << " " << VarName.Declaration << ";\n"; } } TokenMap[VarToDeclare] = VarName.Use.str().str(); } decompilerLog(Out, "End of Top-Scope Declarations"); } // 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 << ptml::tokenTag("uint64_t", tokens::Type) << " " << LoopStateVarDeclaration << ";\n"; // Recursively print the body of this function emitGHASTNode(GHAST.getRoot()); } } Out << "\n"; } static std::string decompileFunction(FunctionMetadataCache &Cache, const llvm::Function &LLVMFunc, const ASTTree &CombedAST, const Binary &Model, const InstrSetVec &TopScopeVariables, bool NeedsLocalStateVar) { std::string Result; llvm::raw_string_ostream Out(Result); CCodeGenerator Backend(Cache, Model, LLVMFunc, CombedAST, TopScopeVariables, Out); Backend.emitFunction(NeedsLocalStateVar); Out.flush(); return Result; } using Container = revng::pipes::DecompiledCCodeInYAMLStringMap; void decompile(FunctionMetadataCache &Cache, llvm::Module &Module, const model::Binary &Model, Container &DecompiledFunctions) { 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(Cache, 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)); } }