#include #include "pseudoc.h" #include "highlevelilinstruction.h" using namespace std; using namespace BinaryNinja; PseudoCFunction::PseudoCFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction), m_typePrinter(type->GetTypePrinter()) { if (!m_typePrinter) { m_typePrinter = TypePrinter::GetDefault(); } } void PseudoCFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) { // Braces must always be turned on for C tokens.SetBraceRequirement(BracesAlwaysRequired); // For switch cases in C, declarations inside aren't scoped by default. Add braces around the cases // to give them scope. tokens.SetBracesAroundSwitchCases(true); tokens.SetHasCollapsableRegions(true); } void PseudoCFunction::BeginLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) { if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) { // At top level, add braces around the entire function tokens.PrependCollapseIndicator(); tokens.AppendOpenBrace(); tokens.IncreaseIndent(); tokens.NewLine(); } } void PseudoCFunction::EndLines(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens) { if (instr.exprIndex == m_highLevelIL->GetRootExpr().exprIndex) { // At top level, add braces around the entire function tokens.NewLine(); tokens.DecreaseIndent(); tokens.PrependCollapseIndicator(); tokens.AppendCloseBrace(); } } BNSymbolDisplayResult PseudoCFunction::AppendPointerTextToken(const HighLevelILInstruction& instr, int64_t val, vector& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence) { Confidence> type = instr.GetType(); if (type.GetValue() && (type->GetClass() == PointerTypeClass) && type->IsConst()) { string stringValue; size_t childWidth = 0; if (auto child = type->GetChildType(); child.GetValue()) childWidth = child->GetWidth(); if (auto strType = GetFunction()->GetView()->CheckForStringAnnotationType(val, stringValue, false, false, childWidth); strType.has_value()) { if (symbolDisplay == DereferenceNonDataSymbols) { if (precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, "("); tokens.emplace_back(OperationToken, "*"); } tokens.emplace_back(BraceToken, DisassemblyTextRenderer::GetStringLiteralPrefix(strType.value()) + string("\"")); tokens.emplace_back(StringToken, StringReferenceTokenContext, stringValue, instr.address, strType.value()); tokens.emplace_back(BraceToken, "\""); if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, ")"); return OtherSymbolResult; } } if (GetFunction()) { // If the pointer has a value of 0, check if it points to a valid address by // 1. If the binary is relocatable, assign the pointer as nullptr // 2. else, check if the constant zero which being referenced is a pointer(display as symbol) or not(display as // nullptr) if (val == 0x0 && type.GetValue() && (type->GetClass() == PointerTypeClass)) { if (GetFunction()->GetView()->IsRelocatable()) { if (symbolDisplay == DereferenceNonDataSymbols) { if (precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, "("); tokens.emplace_back(OperationToken, "*"); } tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, ")"); return OtherSymbolResult; } auto arch = GetHighLevelILFunction()->GetArchitecture(); auto refs = GetHighLevelILFunction()->GetFunction()->GetConstantsReferencedByInstructionIfAvailable( arch, instr.address); bool constantZeroBeingReferencedIsPointer = false; for (const BNConstantReference& ref : refs) if (ref.value == 0x0 && ref.pointer) constantZeroBeingReferencedIsPointer = true; if (!constantZeroBeingReferencedIsPointer) { if (symbolDisplay == DereferenceNonDataSymbols) { if (precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, "("); tokens.emplace_back(OperationToken, "*"); } tokens.emplace_back(CodeSymbolToken, InstructionAddressTokenContext, "nullptr", instr.address, val); if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, ")"); return OtherSymbolResult; } } Ref data = GetFunction()->GetView(); vector symTokens; BNSymbolDisplayResult result = DisassemblyTextRenderer::AddSymbolTokenStatic(symTokens, val, 0, BN_INVALID_OPERAND, data, settings ? settings->GetMaximumSymbolWidth() : 0, GetFunction(), BN_FULL_CONFIDENCE, symbolDisplay, precedence, instr.address); if (result != NoSymbolAvailable) { for (auto& i : symTokens) tokens.emplace_back(i); return result; } } if (symbolDisplay == DereferenceNonDataSymbols) { if (precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, "("); tokens.emplace_back(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.emplace_back(BraceToken, "("); tokens.emplace_back(TypeNameToken, GetSizeToken(instr.size, false)); tokens.emplace_back(OperationToken, "*"); tokens.emplace_back(BraceToken, ")"); } } char valStr[32]; if (val >= 0) { if (val <= 9) snprintf(valStr, sizeof(valStr), "%" PRIx64, val); else snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); } else { if (val >= -9) snprintf(valStr, sizeof(valStr), "-%" PRIx64, val); else snprintf(valStr, sizeof(valStr), "-0x%" PRIx64, val); } tokens.emplace_back(PossibleAddressToken, InstructionAddressTokenContext, valStr, instr.address, val); if (symbolDisplay == DereferenceNonDataSymbols && precedence > UnaryOperatorPrecedence) tokens.emplace_back(BraceToken, ")"); return OtherSymbolResult; } string PseudoCFunction::GetSizeToken(size_t size, bool isSigned) { return GetTypePrinter()->GetTypeString(Type::IntegerType(size, isSigned), nullptr, QualifiedName()); } void PseudoCFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter) { const auto token = GetSizeToken(size, isSigned); if (!token.empty()) emitter.Append(TypeNameToken, token); } void PseudoCFunction::AppendSingleSizeToken( size_t size, BNInstructionTextTokenType type, HighLevelILTokenEmitter& emitter) { char sizeStr[32]; switch (size) { case 0: break; case 1: emitter.Append(type, "B"); break; case 2: emitter.Append(type, "W"); break; case 4: emitter.Append(type, "D"); break; case 8: emitter.Append(type, "Q"); break; case 10: emitter.Append(type, "T"); break; case 16: emitter.Append(type, "O"); break; default: snprintf(sizeStr, sizeof(sizeStr), "%" PRIuPTR "", size); emitter.Append(type, sizeStr); break; } } void PseudoCFunction::AppendComparison(const string& comparison, const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, BNOperatorPrecedence precedence, std::optional signedHint) { const auto leftExpr = instr.GetLeftExpr(); const auto rightExpr = instr.GetRightExpr(); if (leftExpr.operation == HLIL_SPLIT) AppendDefaultSplitExpr(leftExpr, emitter, settings, precedence); else GetExprTextInternal(leftExpr, emitter, settings, precedence, false, signedHint); emitter.Append(OperationToken, comparison); if (rightExpr.operation == HLIL_SPLIT) AppendDefaultSplitExpr(rightExpr, emitter, settings, precedence); else GetExprTextInternal(rightExpr, emitter, settings, precedence, false, signedHint); } void PseudoCFunction::AppendTwoOperand(const string& operand, const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, BNOperatorPrecedence precedence, std::optional signedHint) { const auto& twoOperand = instr.AsTwoOperand(); const auto leftExpr = twoOperand.GetLeftExpr(); const auto rightExpr = twoOperand.GetRightExpr(); BNOperatorPrecedence leftPrecedence = precedence; switch (precedence) { case SubOperatorPrecedence: // Treat left side of subtraction as same level as addition. This lets // (a - b) - c be represented as a - b - c, but a - (b - c) does not // simplify at rendering leftPrecedence = AddOperatorPrecedence; break; case DivideOperatorPrecedence: // Treat left side of divison as same level as multiplication. This lets // (a / b) / c be represented as a / b / c, but a / (b / c) does not // simplify at rendering leftPrecedence = MultiplyOperatorPrecedence; break; default: break; } if (!settings || settings->IsOptionSet(ShowTypeCasts)) { if (leftExpr.operation == HLIL_VAR && (operand == " + " || operand == " - ")) { const auto variableType = GetFunction()->GetVariableType(leftExpr.GetVariable()); if (variableType.GetValue()) { const auto childType = variableType->GetChildType(); if (variableType->IsPointer() && childType.GetValue() && childType->GetWidth() != 1) { emitter.AppendOpenParen(); emitter.Append(TypeNameToken, "char"); emitter.Append(TextToken, "*"); emitter.AppendCloseParen(); } } } } GetExprTextInternal(leftExpr, emitter, settings, leftPrecedence, false, signedHint); auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { return ((1UL << ((width * 8) - 1UL)) & value) != 0; }; if ((operand == " + ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant(), rightExpr.size) && rightExpr.size >= leftExpr.size) { // Convert addition of a negative constant into subtraction of a positive constant emitter.Append(OperationToken, " - "); emitter.AppendIntegerTextToken( rightExpr, -BNSignExtend(rightExpr.GetConstant(), rightExpr.size, 8), rightExpr.size); return; } if ((operand == " - ") && (rightExpr.operation == HLIL_CONST) && lessThanZero(rightExpr.GetConstant(), rightExpr.size) && rightExpr.size >= leftExpr.size) { // Convert subtraction of a negative constant into addition of a positive constant emitter.Append(OperationToken, " + "); emitter.AppendIntegerTextToken( rightExpr, -BNSignExtend(rightExpr.GetConstant(), rightExpr.size, 8), rightExpr.size); return; } emitter.Append(OperationToken, operand); GetExprTextInternal(rightExpr, emitter, settings, precedence, false, signedHint); } void PseudoCFunction::AppendTwoOperandFunction(const string& function, const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings, bool sizeToken) { const auto& twoOperand = instr.AsTwoOperand(); const auto leftExpr = twoOperand.GetLeftExpr(); const auto rightExpr = twoOperand.GetRightExpr(); emitter.Append(OperationToken, function); if (sizeToken) AppendSingleSizeToken(twoOperand.size, OperationToken, emitter); emitter.AppendOpenParen(); if (leftExpr.operation == HLIL_SPLIT) { const auto low = leftExpr.GetLowExpr(); const auto high = leftExpr.GetHighExpr(); emitter.Append(OperationToken, "COMBINE"); emitter.AppendOpenParen(); GetExprTextInternal(high, emitter, settings); emitter.Append(TextToken, ", "); GetExprTextInternal(low, emitter, settings); emitter.AppendCloseParen(); } else { GetExprTextInternal(leftExpr, emitter, settings); } emitter.Append(TextToken, ", "); GetExprTextInternal(rightExpr, emitter, settings); emitter.AppendCloseParen(); } void PseudoCFunction::AppendTwoOperandFunctionWithCarry(const string& function, const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings) { const auto leftExpr = instr.GetLeftExpr(); const auto rightExpr = instr.GetRightExpr(); const auto carryExpr = instr.GetCarryExpr(); tokens.Append(OperationToken, function); AppendSingleSizeToken(instr.size, OperationToken, tokens); tokens.AppendOpenParen(); if (leftExpr.operation == HLIL_SPLIT) { const auto low = leftExpr.GetLowExpr(); const auto high = leftExpr.GetHighExpr(); tokens.Append(OperationToken, "COMBINE"); tokens.AppendOpenParen(); GetExprTextInternal(high, tokens, settings); tokens.Append(TextToken, ", "); GetExprTextInternal(low, tokens, settings); tokens.AppendCloseParen(); } GetExprTextInternal(leftExpr, tokens, settings); tokens.Append(TextToken, ", "); GetExprTextInternal(rightExpr, tokens, settings); tokens.Append(TextToken, ", "); GetExprTextInternal(carryExpr, tokens, settings); tokens.AppendCloseParen(); } Ref PseudoCFunction::GetFieldType(const HighLevelILInstruction& var, bool deref) { Ref type = var.GetType().GetValue(); if (deref && type && (type->GetClass() == PointerTypeClass)) type = type->GetChildType().GetValue(); if (type && (type->GetClass() == NamedTypeReferenceClass)) type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); return type; } PseudoCFunction::FieldDisplayType PseudoCFunction::GetFieldDisplayType( Ref type, uint64_t offset, size_t memberIndex, bool deref) { if (type && (type->GetClass() == StructureTypeClass)) { std::optional memberIndexHint; if (memberIndex != BN_INVALID_EXPR) memberIndexHint = memberIndex; if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, [&](NamedTypeReference*, Structure*, size_t, uint64_t, uint64_t, const StructureMember&) {}), memberIndexHint) return FieldDisplayName; return FieldDisplayOffset; } else if (deref || offset != 0) return FieldDisplayOffset; else return FieldDisplayNone; } std::optional PseudoCFunction::CanSimplifyToTernary(const BinaryNinja::HighLevelILInstruction &instr) const { // Only handle if-statements if (instr.operation != HLIL_IF) return std::nullopt; auto conditionExpr = instr.GetConditionExpr(); auto trueExpr = instr.GetTrueExpr(); auto falseExpr = instr.GetFalseExpr(); if (GetHighLevelILFunction()->HasSideEffects(conditionExpr)) return std::nullopt; // Both branches must be assignment operations if (trueExpr.operation != HLIL_ASSIGN || falseExpr.operation != HLIL_ASSIGN) return std::nullopt; // Get the destination expressions of the assignments auto trueDestExpr = trueExpr.GetDestExpr(); auto falseDestExpr = falseExpr.GetDestExpr(); // Verify that the destination expressions are variable references if (trueDestExpr.operation != HLIL_VAR || falseDestExpr.operation != HLIL_VAR) return std::nullopt; auto trueExprDestExpr = trueExpr.GetDestExpr(); auto falseExprDestExpr = falseExpr.GetDestExpr(); if (trueExprDestExpr.operation != HLIL_VAR || falseExprDestExpr.operation != HLIL_VAR) return std::nullopt; auto trueExprDestVar = trueExprDestExpr.GetVariable(); auto falseExprDestVar = falseExprDestExpr.GetVariable(); if (trueExprDestVar != falseExprDestVar) return std::nullopt; auto trueExprSourceExpr = trueExpr.GetSourceExpr(); auto falseExprSourceExpr = falseExpr.GetSourceExpr(); if (GetHighLevelILFunction()->HasSideEffects(trueExprSourceExpr) || GetHighLevelILFunction()->HasSideEffects(falseExprSourceExpr)) return std::nullopt; // Avoid folding for "else if" cases for (auto parent = instr; parent.HasParent(); parent = parent.GetParent()) { if (parent.operation != HLIL_IF) break; auto parentFalse = parent.GetFalseExpr(); if (parentFalse.operation == HLIL_IF) return std::nullopt; } TernaryInfo info; info.conditional = conditionExpr; info.assignDest = trueDestExpr; info.trueAssign = trueExprSourceExpr; info.falseAssign = falseExprSourceExpr; return info; } bool PseudoCFunction::TryEmitSimplifiedTernary( const BinaryNinja::HighLevelILInstruction &instr, DisassemblySettings* settings, BinaryNinja::HighLevelILTokenEmitter &emitter ) { auto ternaryOpt = CanSimplifyToTernary(instr); if (!ternaryOpt.has_value()) return false; auto &[conditional, assignDest, trueAssign, falseAssign] = ternaryOpt.value(); std::vector tokens = emitter.GetCurrentTokens(); size_t originalTokenCount = tokens.size(); // Emit the destination expression GetExprText(assignDest, emitter, settings, AssignmentOperatorPrecedence); emitter.Append(OperationToken, " = "); // Emit the condition expression GetExprText(conditional, emitter, settings, TernaryOperatorPrecedence); emitter.Append(OperationToken, " ? "); // Emit the true-source expression GetExprText(trueAssign, emitter, settings, TernaryOperatorPrecedence); emitter.Append(OperationToken, " : "); // Emit the false-source expression GetExprText(falseAssign, emitter, settings, TernaryOperatorPrecedence); emitter.Append(TextToken, ";"); // If the ternary expression is too complex (i.e. too many tokens), revert back. if (tokens.size() - originalTokenCount > emitter.GetMaxTernarySimplificationTokens()) { tokens.resize(originalTokenCount); return false; } return true; } void PseudoCFunction::AppendDefaultSplitExpr(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence) { const auto high = instr.GetHighExpr(); const auto low = instr.GetLowExpr(); if (precedence == EqualityOperatorPrecedence) tokens.AppendOpenParen(); tokens.AppendOpenParen(); GetExprTextInternal(high, tokens, settings, precedence); tokens.Append(OperationToken, " << "); tokens.Append(IntegerToken, std::to_string(low.size * 8)); tokens.AppendCloseParen(); tokens.Append(OperationToken, " | "); GetExprTextInternal(low, tokens, settings, precedence); if (precedence == EqualityOperatorPrecedence) tokens.AppendCloseParen(); } void PseudoCFunction::AppendFieldTextTokens(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, std::optional signedHint, bool addrOf) { const auto srcExpr = instr.GetSourceExpr(); const auto fieldOffset = instr.GetOffset(); const auto memberIndex = instr.GetMemberIndex(); const auto type = GetFieldType(srcExpr, false); const auto fieldDisplayType = GetFieldDisplayType(type, fieldOffset, memberIndex, false); if (type && fieldDisplayType == FieldDisplayOffset) { if (!addrOf) tokens.Append(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); AppendSizeToken(!instr.size ? srcExpr.size : instr.size, signedHint.value_or(false), tokens); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } tokens.AppendOpenParen(); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); tokens.Append(TypeNameToken, "char"); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } tokens.Append(OperationToken, "&"); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); tokens.Append(OperationToken, " + "); tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size); tokens.AppendCloseParen(); char offsetStr[64]; snprintf(offsetStr, sizeof(offsetStr), "0x%" PRIx64, fieldOffset); vector nameList {offsetStr}; HighLevelILTokenEmitter::AddNamesForOuterStructureMembers(GetFunction()->GetView(), type, srcExpr, nameList); } else { BNOperatorPrecedence precedence = UnaryOperatorPrecedence; if ((!settings || settings->IsOptionSet(ShowTypeCasts)) && srcExpr.operation == HLIL_ARRAY_INDEX) { auto arrayIndexExpr = srcExpr.GetSourceExpr(); if (arrayIndexExpr.operation == HLIL_VAR) { const Variable var = arrayIndexExpr.GetVariable(); // NOTE: Querying through the variable type instead of expr type because it seems to be missing. auto varTy = GetFunction()->GetVariableType(var).GetValue(); if (varTy && varTy->IsNamedTypeRefer()) varTy = varTy->DerefNamedTypeReference(GetFunction()->GetView()); if (varTy && varTy->GetChildType().GetValue() && varTy->GetChildType()->GetWidth() < instr.size) { if (!addrOf) tokens.Append(TextToken, "*"); tokens.AppendOpenParen(); AppendSizeToken(instr.size, signedHint.value_or(false), tokens); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); tokens.Append(OperationToken, "&"); } else if (addrOf) { tokens.Append(OperationToken, "&"); } } else if (addrOf) { tokens.Append(OperationToken, "&"); } } else if ((!settings || settings->IsOptionSet(ShowTypeCasts)) && srcExpr.operation == HLIL_VAR) { const Variable var = srcExpr.GetVariable(); // NOTE: Querying through the variable type instead of expr type because it seems to be missing. auto varTy = GetFunction()->GetVariableType(var).GetValue(); if (varTy && varTy->IsNamedTypeRefer()) varTy = varTy->DerefNamedTypeReference(GetFunction()->GetView()); if (varTy && varTy->GetClass() != StructureTypeClass && srcExpr.size > instr.size) { if (addrOf) tokens.Append(OperationToken, "&"); tokens.AppendOpenParen(); AppendSizeToken(instr.size, signedHint.value_or(false), tokens); tokens.AppendCloseParen(); precedence = MemberAndFunctionOperatorPrecedence; } else if (addrOf) { tokens.Append(OperationToken, "&"); } } else if (addrOf) { tokens.Append(OperationToken, "&"); } GetExprTextInternal(srcExpr, tokens, settings, precedence); } switch (fieldDisplayType) { case FieldDisplayName: { std::optional memberIndexHint; if (memberIndex != BN_INVALID_EXPR) memberIndexHint = memberIndex; if (type && type->GetStructure()->ResolveMemberOrBaseMember( GetFunction()->GetView(), fieldOffset, 0, [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member) { tokens.Append(OperationToken, "."); vector nameList {member.name}; HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( GetFunction()->GetView(), type, srcExpr, nameList); tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, BN_FULL_CONFIDENCE, nameList); }, memberIndexHint)) return; // Part of structure but no defined field, use __offset syntax tokens.Append(OperationToken, "."); char offsetStr[64]; snprintf(offsetStr, sizeof(offsetStr), "__offset(0x%" PRIx64 ")%s", fieldOffset, Type::GetSizeSuffix(instr.size).c_str()); vector nameList {offsetStr}; HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( GetFunction()->GetView(), type, srcExpr, nameList); tokens.Append(StructOffsetToken, offsetStr, fieldOffset, instr.size, 0, BN_FULL_CONFIDENCE, nameList); return; } case FieldDisplayOffset: { /* this is handled before the display */ return; } default: break; } } void PseudoCFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement) { GetExprTextInternal(instr, tokens, settings, precedence, statement); } void PseudoCFunction::GetExprTextInternal(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement, optional signedHint) { // The lambdas in this function are here to reduce stack frame size of this function. Without them, // complex expression can cause the process to crash from a stack overflow. auto exprGuard = tokens.SetCurrentExpr(instr); if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType().GetValue()) { tokens.AppendOpenParen(); tokens.AppendOpenParen(); auto typeTokens = GetTypePrinter()->GetTypeTokens( instr.GetType().GetValue(), GetArchitecture()->GetStandalonePlatform(), QualifiedName()); for (auto& token: typeTokens) { tokens.Append(token); } tokens.AppendCloseParen(); tokens.Append(TextToken, " "); } if (settings && settings->IsOptionSet(ShowILOpcodes)) { tokens.Append(OperationToken, "/*"); tokens.Append(OperationToken, fmt::format("{}", instr.operation)); tokens.Append(OperationToken, "*/"); tokens.Append(TextToken, " "); } auto function = m_highLevelIL->GetFunction(); if (instr.ast) tokens.PrependCollapseIndicator(function, instr); // ensure we claim the line address for top level instrs on a line if (instr.operation != HLIL_BLOCK) tokens.InitLine(); switch (instr.operation) { case HLIL_BLOCK: [&]() { const auto exprs = instr.GetBlockExprs(); bool needSeparator = false; for (auto i = exprs.begin(); i != exprs.end(); ++i) { // Don't show void returns at the very end of the function when printing // the root of an AST, as it is implicit and almost always omitted in // normal source code. auto next = i; ++next; if (instr.ast && (instr.exprIndex == GetHighLevelILFunction()->GetRootExpr().exprIndex) && (exprs.size() > 1) && (next == exprs.end()) && ((*i).operation == HLIL_RET) && ((*i).GetSourceExprs().size() == 0)) continue; // If the statement is one that contains additional blocks of code, insert a scope separator // to visually separate the logic. bool hasBlocks = false; switch ((*i).operation) { case HLIL_IF: case HLIL_WHILE: case HLIL_WHILE_SSA: case HLIL_DO_WHILE: case HLIL_DO_WHILE_SSA: case HLIL_FOR: case HLIL_FOR_SSA: case HLIL_SWITCH: hasBlocks = true; break; default: hasBlocks = false; break; } if (needSeparator || (i != exprs.begin() && hasBlocks)) { tokens.ScopeSeparator(); } needSeparator = hasBlocks; // Emit the lines for the statement itself if (!ShouldSkipStatement(*i)) { GetExprTextInternal(*i, tokens, settings, TopLevelOperatorPrecedence, true); tokens.NewLine(); } } }(); break; case HLIL_FOR: [&]() { const auto initExpr = instr.GetInitExpr(); const auto condExpr = instr.GetConditionExpr(); const auto updateExpr = instr.GetUpdateExpr(); const auto loopExpr = instr.GetLoopExpr(); if (instr.ast) { tokens.Append(KeywordToken, "for "); tokens.AppendOpenParen(); if (initExpr.operation != HLIL_NOP) GetExprTextInternal(initExpr, tokens, settings); tokens.Append(TextToken, "; "); if (condExpr.operation != HLIL_NOP) GetExprTextInternal(condExpr, tokens, settings); tokens.Append(TextToken, "; "); if (updateExpr.operation != HLIL_NOP) GetExprTextInternal(updateExpr, tokens, settings); tokens.AppendCloseParen(); if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprTextInternal(loopExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(scopeType); tokens.FinalizeScope(); } } else { tokens.Append(KeywordToken, "while "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); } }(); break; case HLIL_IF: [&]() { if (instr.ast && TryEmitSimplifiedTernary(instr, settings, tokens)) return; const auto condExpr = instr.GetConditionExpr(); const auto trueExpr = instr.GetTrueExpr(); const auto falseExpr = instr.GetFalseExpr(); tokens.Append(KeywordToken, "if "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); tokens.BeginScope(scopeType); GetExprTextInternal(trueExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(scopeType); } //tokens.SetCurrentExpr(falseExpr); if (falseExpr.operation == HLIL_IF) { tokens.ScopeContinuation(false); tokens.Append(KeywordToken, "else "); GetExprTextInternal(falseExpr, tokens, settings, TopLevelOperatorPrecedence, true); } else if (falseExpr.operation != HLIL_NOP) { tokens.ScopeContinuation(false); tokens.PrependCollapseIndicator(function, instr, 1); tokens.Append(KeywordToken, "else"); if (function->IsInstructionCollapsed(instr, 1)) { tokens.Append(CollapsedInformationToken, " {...}"); tokens.NewLine(); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); tokens.BeginScope(scopeType); GetExprTextInternal(falseExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(scopeType); tokens.FinalizeScope(); } } else { tokens.FinalizeScope(); } }(); break; case HLIL_WHILE: [&]() { const auto condExpr = instr.GetConditionExpr(); const auto loopExpr = instr.GetLoopExpr(); tokens.Append(KeywordToken, "while "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprTextInternal(loopExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(scopeType); tokens.FinalizeScope(); } }(); break; case HLIL_DO_WHILE: [&]() { const auto loopExpr = instr.GetLoopExpr(); const auto condExpr = instr.GetConditionExpr(); if (instr.ast) { tokens.Append(KeywordToken, "do"); if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); tokens.NewLine(); tokens.Append(KeywordToken, "while "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); tokens.Append(KeywordToken, ";"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprTextInternal(loopExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(scopeType); tokens.ScopeContinuation(true); tokens.Append(KeywordToken, "while "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); tokens.Append(KeywordToken, ";"); tokens.FinalizeScope(); } } else { tokens.Append(TextToken, "/* do */ "); tokens.Append(KeywordToken, "while "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); } }(); break; case HLIL_SWITCH: [&]() { const auto condExpr = instr.GetConditionExpr(); const auto caseExprs = instr.GetCases(); const auto defaultExpr = instr.GetDefaultExpr(); tokens.Append(KeywordToken, "switch "); tokens.AppendOpenParen(); GetExprTextInternal(condExpr, tokens, settings); tokens.AppendCloseParen(); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { tokens.BeginScope(SwitchScopeType); for (const auto caseExpr: caseExprs) { GetExprTextInternal(caseExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.NewLine(); } if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) { tokens.PrependCollapseIndicator(function, instr, 1); tokens.Append(KeywordToken, "default"); tokens.Append(TextToken, ":"); if (function->IsInstructionCollapsed(instr, 1)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { tokens.BeginScope(CaseScopeType); GetExprTextInternal(defaultExpr, tokens, settings, TopLevelOperatorPrecedence, true); tokens.EndScope(CaseScopeType); tokens.FinalizeScope(); } } tokens.EndScope(SwitchScopeType); tokens.FinalizeScope(); } }(); break; case HLIL_CASE: [&]() { const auto valueExprs = instr.GetValueExprs(); const auto trueExpr = instr.GetTrueExpr(); for (size_t index{}; index < valueExprs.size(); index++) { const auto& valueExpr = valueExprs[index]; tokens.Append(KeywordToken, "case "); GetExprTextInternal(valueExpr, tokens, settings); tokens.Append(TextToken, ":"); if (index != valueExprs.size() - 1) tokens.NewLine(); } if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { tokens.PrependCollapseIndicator(function, instr); tokens.BeginScope(CaseScopeType); GetExprTextInternal(trueExpr, tokens, settings, TopLevelOperatorPrecedence, true); static const std::vector operations { HLIL_CONTINUE, HLIL_NORET, HLIL_UNREACHABLE, HLIL_JUMP, HLIL_GOTO, HLIL_TAILCALL}; // If the case doesn't have an instruction that implicitly exits the case, append a break statement // at the end of the case if (trueExpr.operation == HLIL_BLOCK) { const auto& blockExprs = trueExpr.GetBlockExprs(); bool disallowedOperation = false; for (const auto expr : blockExprs) { if (std::find(operations.begin(), operations.end(), expr.operation) != operations.end()) { disallowedOperation = true; break; } } if (!disallowedOperation) { tokens.NewLine(); tokens.PrependCollapseIndicator(); tokens.Append(KeywordToken, "break"); tokens.Append(TextToken, ";"); } } else if (std::find(operations.begin(), operations.end(), trueExpr.operation) == operations.end()) { tokens.NewLine(); tokens.PrependCollapseIndicator(); tokens.Append(KeywordToken, "break"); tokens.Append(TextToken, ";"); } tokens.EndScope(CaseScopeType); tokens.FinalizeScope(); } }(); break; case HLIL_BREAK: [&]() { tokens.Append(KeywordToken, "break"); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CONTINUE: [&]() { tokens.Append(KeywordToken, "continue"); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ZX: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); AppendSizeToken(instr.size, false, tokens); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence, false, false); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_SX: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); AppendSizeToken(instr.size, true, tokens); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence, false, true); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CALL: GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement); break; case HLIL_IMPORT: GetExpr_IMPORT(instr, tokens, settings, precedence, statement); break; case HLIL_ARRAY_INDEX: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto indexExpr = instr.GetIndexExpr(); GetExprTextInternal(srcExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendOpenBracket(); GetExprTextInternal(indexExpr, tokens, settings); tokens.AppendCloseBracket(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_VAR_INIT: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto destExpr = instr.GetDestVariable(); const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(destExpr); const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); const auto prevTypeTokens = variableType.GetValue() ? GetTypePrinter()->GetTypeTokensBeforeName( variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; const auto postTypeTokens = variableType.GetValue() ? GetTypePrinter()->GetTypeTokensAfterName( variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; // Check to see if the variable appears live bool appearsDead = false; if (const auto ssaForm = instr.GetSSAForm(); ssaForm.operation == HLIL_VAR_INIT_SSA) { const auto ssaDest = ssaForm.GetDestSSAVariable(); appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); } // If the variable does not appear live, show the assignment as zero confidence (grayed out) if (appearsDead) tokens.BeginForceZeroConfidence(); if (variableType.GetValue()) { for (auto typeToken: prevTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = destExpr.ToIdentifier(); tokens.Append(typeToken); } tokens.Append(TextToken, " "); } tokens.AppendVarTextToken(destExpr, instr, instr.size); if (variableType.GetValue()) { for (auto typeToken: postTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = destExpr.ToIdentifier(); tokens.Append(typeToken); } } tokens.Append(OperationToken, " = "); // For the right side of the assignment, only use zero confidence if the instruction does // not have any side effects if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) { tokens.EndForceZeroConfidence(); appearsDead = false; } GetExprTextInternal(srcExpr, tokens, settings, AssignmentOperatorPrecedence); if (appearsDead) tokens.EndForceZeroConfidence(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_VAR_DECLARE: [&]() { const auto variable = instr.GetVariable(); const auto variableType = GetHighLevelILFunction()->GetFunction()->GetVariableType(variable); const auto platform = GetHighLevelILFunction()->GetFunction()->GetPlatform(); const auto prevTypeTokens = variableType.GetValue() ? GetTypePrinter()->GetTypeTokensBeforeName( variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; const auto postTypeTokens = variableType.GetValue() ? GetTypePrinter()->GetTypeTokensAfterName( variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; if (variableType.GetValue()) { for (auto typeToken: prevTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = variable.ToIdentifier(); tokens.Append(typeToken); } tokens.Append(TextToken, " "); } tokens.AppendVarTextToken(variable, instr, instr.size); if (variableType.GetValue()) { for (auto typeToken: postTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = variable.ToIdentifier(); tokens.Append(typeToken); } } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FLOAT_CONST: [&]() { const auto constant = instr.GetConstant(); if (instr.size == 4) { char valueStr[64]; union { float f; uint32_t i; } bits{}; bits.i = constant; snprintf(valueStr, sizeof(valueStr), "%.9gf", bits.f); tokens.Append(FloatingPointToken, InstructionAddressTokenContext, valueStr, instr.address); } else if (instr.size == 8) { char valueStr[64]; union { double f; uint64_t i; } bits{}; bits.i = constant; snprintf(valueStr, sizeof(valueStr), "%.17g", bits.f); string s = valueStr; if ((s.find('.') == string::npos) && (s.find('e') == string::npos)) s += ".0"; tokens.Append(FloatingPointToken, InstructionAddressTokenContext, s, instr.address); } else { tokens.AppendIntegerTextToken(instr, constant, 8); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CONST: [&]() { tokens.AppendConstantTextToken(instr, instr.GetConstant(), instr.size, settings, precedence); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CONST_DATA: [&]() { // Constant data should be rendered according to the type of builtin function being used. const ConstantData& data = instr.GetConstantData(); if (auto [db, builtin] = data.ToDataBuffer(); db.GetLength()) { bool nullTerminates = true; switch (builtin) { case BuiltinStrcpy: case BuiltinStrncpy: { string result(db.ToEscapedString(true)); tokens.Append(BraceToken, "\""); tokens.Append(StringToken, ConstStringDataTokenContext, result, instr.address, data.value); tokens.Append(BraceToken, "\""); break; } case BuiltinMemset: { char buf[32]; if (data.value < 10) snprintf(buf, sizeof(buf), "%" PRId64 "", data.value); else snprintf(buf, sizeof(buf), "0x%" PRIx64 "", data.value); tokens.Append(BraceToken, "{"); tokens.Append(StringToken, ConstDataTokenContext, string(buf), instr.address, data.value); tokens.Append(BraceToken, "}"); break; } case BuiltinWmemcpy: nullTerminates = false; // FALL_THROUGH default: { if (auto unicode = GetFunction()->GetView()->StringifyUnicodeData(instr.function->GetArchitecture(), db, nullTerminates); unicode.has_value()) { auto wideStringPrefix = (builtin == BuiltinWcscpy) ? "L" : ""; auto tokenContext = (builtin == BuiltinWcscpy) ? ConstStringDataTokenContext : ConstDataTokenContext; tokens.Append(BraceToken, wideStringPrefix + string("\"")); tokens.Append(StringToken, tokenContext, unicode.value().first, instr.address, data.value); tokens.Append(BraceToken, "\""); } else { string result(db.ToEscapedString(false, true)); tokens.Append(BraceToken, "\""); tokens.Append(StringToken, ConstDataTokenContext, result, instr.address, data.value); tokens.Append(BraceToken, "\""); // TODO controls for emitting an initializer list? // char str[32]; // string result; // const uint8_t* bytes = (const uint8_t*)db.GetData(); // for (size_t i = 0; i < db.GetLength(); i++) // { // snprintf(str, sizeof(str), "0x%" PRIx8 ", ", bytes[i]); // result += str; // } // if (result.size() > 2) // result.erase(result.end() - 2); // tokens.Append(StringToken, StringDisplayTokenContext, string("{ ") + result + string(" }"), instr.address, bytes.value); } break; } } } else tokens.Append(StringToken, StringDisplayTokenContext, string(""), instr.address, data.value); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CONST_PTR: GetExpr_CONST_PTR(instr, tokens, settings, precedence, statement); break; case HLIL_VAR: [&]() { const auto variable = instr.GetVariable(); const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable); tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName, instr.exprIndex, variable.ToIdentifier(), instr.size); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ASSIGN: [&]() { const auto destExpr = instr.GetDestExpr(); const auto srcExpr = instr.GetSourceExpr(); // Check to see if the variable appears live bool appearsDead = false; if (destExpr.operation == HLIL_VAR_SSA) { const auto ssaForm = destExpr.GetSSAVariable(); appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaForm); } else if (destExpr.operation == HLIL_VAR) { if (const auto ssaForm = destExpr.GetSSAForm(); ssaForm.operation == HLIL_VAR_SSA) { const auto ssaDest = ssaForm.GetSSAVariable(); appearsDead = !GetHighLevelILFunction()->IsSSAVarLive(ssaDest); } } // If the variable does not appear live, show the assignment as zero confidence (grayed out) if (appearsDead) tokens.BeginForceZeroConfidence(); std::optional assignUpdateOperator; std::optional assignUpdateSource; bool assignUpdateNegate = false; const auto destIsSplit = destExpr.operation == HLIL_SPLIT; std::optional assignSignedHint; if (destIsSplit) { const auto high = destExpr.GetHighExpr(); const auto low = destExpr.GetLowExpr(); GetExprTextInternal(high, tokens, settings, precedence); tokens.Append(OperationToken, " = "); tokens.Append(OperationToken, "HIGH"); AppendSingleSizeToken(high.size, OperationToken, tokens); tokens.AppendOpenParen(); GetExprTextInternal(srcExpr, tokens, settings, precedence); tokens.AppendCloseParen(); tokens.AppendSemicolon(); tokens.NewLine(); GetExprTextInternal(low, tokens, settings, precedence); } else if (srcExpr.operation == HLIL_SPLIT) { GetExprTextInternal(destExpr, tokens, settings, precedence); tokens.Append(OperationToken, " = "); AppendDefaultSplitExpr(srcExpr, tokens, settings, precedence); tokens.AppendSemicolon(); return; } else { // Check for assignment with an operator on the same variable as the destination // (for example, `a = a + 2` should be shown as `a += 2`) if ((srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_SUB || srcExpr.operation == HLIL_MUL || srcExpr.operation == HLIL_DIVU || srcExpr.operation == HLIL_DIVS || srcExpr.operation == HLIL_LSL || srcExpr.operation == HLIL_LSR || srcExpr.operation == HLIL_ASR || (instr.size != 0 && srcExpr.operation == HLIL_AND) || (instr.size != 0 && srcExpr.operation == HLIL_OR) || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) && (srcExpr.GetLeftExpr() == destExpr)) { auto lessThanZero = [](uint64_t value, uint64_t width) -> bool { return ((1UL << ((width * 8) - 1UL)) & value) != 0; }; switch (srcExpr.operation) { case HLIL_ADD: assignUpdateOperator = " += "; assignUpdateSource = srcExpr.GetRightExpr(); if ((assignUpdateSource.value().operation == HLIL_CONST) && lessThanZero( assignUpdateSource.value().GetConstant(), assignUpdateSource.value().size) && assignUpdateSource.value().size >= instr.size) { // Convert addition of a negative constant into subtraction of a positive constant assignUpdateOperator = " -= "; assignUpdateNegate = true; } break; case HLIL_SUB: assignUpdateOperator = " -= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_MUL: assignUpdateOperator = " *= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_DIVU: assignUpdateOperator = " u/= "; assignUpdateSource = srcExpr.GetRightExpr(); assignSignedHint = false; break; case HLIL_DIVS: assignUpdateOperator = " s/= "; assignUpdateSource = srcExpr.GetRightExpr(); assignSignedHint = true; break; case HLIL_LSL: assignUpdateOperator = " <<= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_LSR: assignUpdateOperator = " u>>= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_ASR: assignUpdateOperator = " s>>= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_AND: assignUpdateOperator = " &= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_OR: assignUpdateOperator = " |= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_XOR: assignUpdateOperator = " ^= "; assignUpdateSource = srcExpr.GetRightExpr(); break; default: break; } } else if ( (srcExpr.operation == HLIL_ADD || srcExpr.operation == HLIL_MUL || (instr.size != 0 && srcExpr.operation == HLIL_AND) || (instr.size != 0 && srcExpr.operation == HLIL_OR) || (instr.size != 0 && srcExpr.operation == HLIL_XOR)) && (srcExpr.GetRightExpr() == destExpr)) { switch (srcExpr.operation) { case HLIL_ADD: assignUpdateOperator = " += "; assignUpdateSource = srcExpr.GetLeftExpr(); break; case HLIL_MUL: assignUpdateOperator = " *= "; assignUpdateSource = srcExpr.GetLeftExpr(); break; case HLIL_AND: assignUpdateOperator = " &= "; assignUpdateSource = srcExpr.GetLeftExpr(); break; case HLIL_OR: assignUpdateOperator = " |= "; assignUpdateSource = srcExpr.GetLeftExpr(); break; case HLIL_XOR: assignUpdateOperator = " ^= "; assignUpdateSource = srcExpr.GetLeftExpr(); break; default: break; } } } if (!destIsSplit) GetExprTextInternal(destExpr, tokens, settings, precedence); if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) tokens.Append(OperationToken, assignUpdateOperator.value()); else tokens.Append(OperationToken, " = "); // For the right side of the assignment, only use zero confidence if the instruction does // not have any side effects if (appearsDead && GetHighLevelILFunction()->HasSideEffects(srcExpr)) { tokens.EndForceZeroConfidence(); appearsDead = false; } if (destIsSplit) { // const auto high = destExpr.GetHighExpr(); const auto low = destExpr.GetLowExpr(); tokens.Append(OperationToken, "LOW"); AppendSingleSizeToken(low.size, OperationToken, tokens); tokens.AppendOpenParen(); } if (assignUpdateOperator.has_value() && assignUpdateSource.has_value()) { if (assignUpdateNegate) { tokens.AppendIntegerTextToken(assignUpdateSource.value(), -BNSignExtend( assignUpdateSource.value().GetConstant(), assignUpdateSource.value().size, 8), assignUpdateSource.value().size); } else { GetExprTextInternal(assignUpdateSource.value(), tokens, settings, AssignmentOperatorPrecedence, false, assignSignedHint); } } else { GetExprTextInternal(srcExpr, tokens, settings, AssignmentOperatorPrecedence, false, assignSignedHint); } if (destIsSplit) tokens.AppendCloseParen(); if (appearsDead) tokens.EndForceZeroConfidence(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ASSIGN_UNPACK: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto destExprs = instr.GetDestExprs(); const auto firstExpr = destExprs[0]; GetExprTextInternal(firstExpr, tokens, settings, AssignmentOperatorPrecedence); tokens.Append(OperationToken, " = "); GetExprTextInternal(srcExpr, tokens, settings, AssignmentOperatorPrecedence); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_STRUCT_FIELD: [&]() { AppendFieldTextTokens(instr, tokens, settings, signedHint, false); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_DEREF: [&]() { auto srcExpr = instr.GetSourceExpr(); auto appendMaybeBrace = [&](const InstructionTextToken& token) { if (token.type == BraceToken) { if (token.text == "(") tokens.AppendOpenParen(); else if (token.text == ")") tokens.AppendCloseParen(); else if (token.text == "[") tokens.AppendOpenBracket(); else if (token.text == "]") tokens.AppendCloseBracket(); else if (token.text == "{") tokens.AppendOpenBrace(); else if (token.text == "}") tokens.AppendCloseBrace(); else tokens.Append(token); } else { tokens.Append(token); } }; if (srcExpr.operation == HLIL_CONST_PTR) { const auto constant = srcExpr.GetConstant(); vector pointerTokens{}; if (AppendPointerTextToken(instr, constant, pointerTokens, settings, DereferenceNonDataSymbols, precedence) == DataSymbolResult) { const auto type = srcExpr.GetType(); if (type.GetValue() && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth()) { if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); } tokens.Append(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); AppendSizeToken(instr.size, false, tokens); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } for (const auto& token : pointerTokens) { appendMaybeBrace(token); } if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendCloseParen(); } } else { for (const auto& token : pointerTokens) { appendMaybeBrace(token); } } } else { for (const auto& token : pointerTokens) { appendMaybeBrace(token); } } } else { size_t derefCount{}; while (srcExpr.operation == HLIL_DEREF) { auto next = srcExpr.GetSourceExpr(); if (next.size == srcExpr.size) { srcExpr = srcExpr.GetSourceExpr(); derefCount++; } else { break; } } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); for (size_t index{}; index <= derefCount; index++) tokens.Append(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); AppendSizeToken(instr.size, false, tokens); for (size_t index{}; index <= derefCount; index++) tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_TAILCALL: [&]() { const auto destExpr = instr.GetDestExpr(); const auto parameterExprs = instr.GetParameterExprs(); tokens.Append(AnnotationToken, "/* tailcall */"); tokens.NewLine(); tokens.Append(KeywordToken, "return "); GetExpr_CALL_OR_TAILCALL(instr, tokens, settings, precedence, statement); }(); break; case HLIL_ADDRESS_OF: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); if (srcExpr.operation == HLIL_STRUCT_FIELD) { AppendFieldTextTokens(srcExpr, tokens, settings, signedHint, true); } else { tokens.Append(OperationToken, "&"); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); } if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_PASS_BY_REF: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (srcExpr.operation == HLIL_ADDRESS_OF) { GetExprTextInternal(srcExpr.GetSourceExpr(), tokens, settings, precedence); } else { tokens.Append(OperationToken, "*"); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_RETURN_BY_REF: [&]() { const auto srcExpr = instr.GetSourceExpr(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_E: [&]() { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" == ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CMP_E: [&]() { if ((instr.GetRightExpr().operation == HLIL_CONST || instr.GetRightExpr().operation == HLIL_CONST_PTR) && instr.GetRightExpr().GetConstant() == 0) { bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.Append(OperationToken, "!"); GetExprTextInternal(instr.GetLeftExpr(), tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); } else { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" == ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_NE: [&]() { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" != ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CMP_NE: [&]() { if ((instr.GetRightExpr().operation == HLIL_CONST || instr.GetRightExpr().operation == HLIL_CONST_PTR) && instr.GetRightExpr().GetConstant() == 0) { GetExprTextInternal(instr.GetLeftExpr(), tokens, settings, precedence); } else { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" != ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_LT: case HLIL_CMP_SLT: case HLIL_CMP_ULT: [&]() { bool parens = precedence > CompareOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional cmpSigned; if (instr.operation == HLIL_CMP_ULT) cmpSigned = false; else if (instr.operation == HLIL_CMP_SLT) cmpSigned = true; AppendComparison(" < ", instr, tokens, settings, CompareOperatorPrecedence, cmpSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_LE: case HLIL_CMP_SLE: case HLIL_CMP_ULE: [&]() { bool parens = precedence > CompareOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional cmpSigned; if (instr.operation == HLIL_CMP_ULE) cmpSigned = false; else if (instr.operation == HLIL_CMP_SLE) cmpSigned = true; AppendComparison(" <= ", instr, tokens, settings, CompareOperatorPrecedence, cmpSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_GE: case HLIL_CMP_SGE: case HLIL_CMP_UGE: [&]() { bool parens = precedence > CompareOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional cmpSigned; if (instr.operation == HLIL_CMP_UGE) cmpSigned = false; else if (instr.operation == HLIL_CMP_SGE) cmpSigned = true; AppendComparison(" >= ", instr, tokens, settings, CompareOperatorPrecedence, cmpSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_GT: case HLIL_CMP_SGT: case HLIL_CMP_UGT: [&]() { bool parens = precedence > CompareOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional cmpSigned; if (instr.operation == HLIL_CMP_UGT) cmpSigned = false; else if (instr.operation == HLIL_CMP_SGT) cmpSigned = true; AppendComparison(" > ", instr, tokens, settings, CompareOperatorPrecedence, cmpSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_AND: [&]() { bool parens = instr.size == 0 ? precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalOrOperatorPrecedence : precedence >= EqualityOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(instr.size == 0 ? " && " : " & ", instr, tokens, settings, instr.size == 0 ? LogicalAndOperatorPrecedence : BitwiseAndOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_OR: [&]() { bool parens = (instr.size == 0) ? precedence >= BitwiseOrOperatorPrecedence || precedence == LogicalAndOperatorPrecedence : precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(instr.size == 0 ? " || " : " | ", instr, tokens, settings, instr.size == 0 ? LogicalOrOperatorPrecedence : BitwiseOrOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_XOR: [&]() { bool parens = precedence >= EqualityOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" ^ ", instr, tokens, settings, BitwiseXorOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ADC: case HLIL_ADD_OVERFLOW: case HLIL_FADD: case HLIL_ADD: [&]() { bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" + ", instr, tokens, settings, AddOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_SUB: [&]{ // Check for offset pointers auto left = instr.GetLeftExpr(); auto right = instr.GetRightExpr(); if (left.operation == HLIL_VAR && right.operation == HLIL_CONST) { auto var = left.GetVariable(); auto srcOffset = right.GetConstant(); auto varType = GetFunction()->GetVariableType(var); if (varType.GetValue() && varType->GetClass() == PointerTypeClass && varType->GetNamedTypeReference() && varType->GetOffset() == srcOffset) { // Yes tokens.Append(OperationToken, "ADJ"); tokens.AppendOpenParen(); GetExprTextInternal(left, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendCloseParen(); return; } } // No bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" - ", instr, tokens, settings, SubOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_SBB: case HLIL_FSUB: [&]() { bool parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" - ", instr, tokens, settings, SubOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_LSL: [&]() { bool parens = precedence > ShiftOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" << ", instr, tokens, settings, ShiftOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_LSR: case HLIL_ASR: [&]() { bool parens = precedence > ShiftOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" >> ", instr, tokens, settings, ShiftOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FMUL: case HLIL_MUL: case HLIL_MULU_DP: case HLIL_MULS_DP: [&]() { bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional mulSigned; if (instr.operation == HLIL_MULU_DP) mulSigned = false; else if (instr.operation == HLIL_MULS_DP) mulSigned = true; AppendTwoOperand(" * ", instr, tokens, settings, MultiplyOperatorPrecedence, mulSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FDIV: case HLIL_DIVU: case HLIL_DIVU_DP: case HLIL_DIVS: case HLIL_DIVS_DP: [&]() { bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional divSigned; if (instr.operation == HLIL_DIVU || instr.operation == HLIL_DIVU_DP) divSigned = false; else if (instr.operation == HLIL_DIVS || instr.operation == HLIL_DIVS_DP) divSigned = true; AppendTwoOperand(" / ", instr, tokens, settings, DivideOperatorPrecedence, divSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_MODU: case HLIL_MODU_DP: case HLIL_MODS: case HLIL_MODS_DP: [&]() { bool parens = precedence > MultiplyOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; if (parens) tokens.AppendOpenParen(); std::optional modSigned; if (instr.operation == HLIL_MODU || instr.operation == HLIL_MODU_DP) modSigned = false; else if (instr.operation == HLIL_MODS || instr.operation == HLIL_MODS_DP) modSigned = true; AppendTwoOperand(" % ", instr, tokens, settings, DivideOperatorPrecedence, modSigned); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ROR: [&]() { AppendTwoOperandFunction("ROR", instr, tokens, settings); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ROL: [&]() { AppendTwoOperandFunction("ROL", instr, tokens, settings); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_RLC: [&]() { AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_RRC: [&]() { AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_TEST_BIT: [&]() { AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FLOOR: [&]() { tokens.Append(OperationToken, "floor"); tokens.AppendOpenParen(); GetExprTextInternal(instr.GetSourceExpr(), tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CEIL: [&]() { tokens.Append(OperationToken, "ceil"); tokens.AppendOpenParen(); GetExprTextInternal(instr.GetSourceExpr(), tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FTRUNC: [&]() { auto src = instr.GetSourceExpr(); string trunc; if (src.size == 4) trunc = "truncf"; else if (src.size == 8) trunc = "trunc"; else if (src.size == 10) trunc = "truncl"; else trunc = "trunc" + std::to_string(src.size) + "f"; tokens.Append(OperationToken, trunc); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FABS: [&]() { auto src = instr.GetSourceExpr(); string fabs; if (src.size == 4) fabs = "fabsf"; else if (src.size == 8) fabs = "fabs"; else if (src.size == 10) fabs = "fabsl"; else fabs = "fabs" + std::to_string(src.size) + "f"; tokens.Append(OperationToken, fabs); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FSQRT: [&]() { auto src = instr.GetSourceExpr(); string sqrt; if (src.size == 4) sqrt = "sqrtf"; else if (src.size == 8) sqrt = "sqrt"; else if (src.size == 10) sqrt = "sqrtl"; else sqrt = "sqrt" + std::to_string(src.size) + "f"; tokens.Append(OperationToken, sqrt); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_ROUND_TO_INT: [&]() { auto src = instr.GetSourceExpr(); string round; if (src.size == 4) round = "roundf"; else if (src.size == 8) round = "round"; else if (src.size == 10) round = "roundl"; else round = "round" + std::to_string(src.size) + "f"; tokens.Append(OperationToken, round); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_O: [&]() { AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_UO: [&]() { AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_NOT: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.Append(OperationToken, instr.size == 0 ? "!" : "~"); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FNEG: case HLIL_NEG: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.Append(OperationToken, "-"); tokens.AppendOpenParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence, false, true); tokens.AppendCloseParen(); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_BSWAP: [&]() { auto src = instr.GetSourceExpr(); string name; if (src.size == 2) name = "__builtin_bswap16"; else if (src.size == 4) name = "__builtin_bswap32"; else if (src.size == 8) name = "__builtin_bswap64"; else name = "__builtin_bswap"; tokens.Append(OperationToken, name); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_POPCNT: [&]() { auto src = instr.GetSourceExpr(); tokens.Append(OperationToken, src.size > 4 ? "__builtin_popcountll" : "__builtin_popcount"); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CLZ: [&]() { auto src = instr.GetSourceExpr(); tokens.Append(OperationToken, src.size > 4 ? "__builtin_clzll" : "__builtin_clz"); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CTZ: [&]() { auto src = instr.GetSourceExpr(); tokens.Append(OperationToken, src.size > 4 ? "__builtin_ctzll" : "__builtin_ctz"); tokens.AppendOpenParen(); GetExprTextInternal(src, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_RBIT: [&]() { tokens.Append(OperationToken, "__rbit"); tokens.AppendOpenParen(); GetExprTextInternal(instr.GetSourceExpr(), tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_CLS: [&]() { tokens.Append(OperationToken, "__cls"); tokens.AppendOpenParen(); GetExprTextInternal(instr.GetSourceExpr(), tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_MINS: case HLIL_MINU: AppendTwoOperandFunction("min", instr, tokens, settings, false); if (statement) tokens.AppendSemicolon(); break; case HLIL_MAXS: case HLIL_MAXU: AppendTwoOperandFunction("max", instr, tokens, settings, false); if (statement) tokens.AppendSemicolon(); break; case HLIL_ABS: [&]() { tokens.Append(OperationToken, "abs"); tokens.AppendOpenParen(); GetExprTextInternal(instr.GetSourceExpr(), tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FLOAT_CONV: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } const auto floatType = instr.size == 2 ? "float16_t" : instr.size == 4 ? "float" : instr.size == 8 ? "double" : instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t"; bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); tokens.Append(TypeNameToken, floatType.c_str()); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_FLOAT_TO_INT: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); AppendSizeToken(instr.size, true, tokens); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_BOOL_TO_INT: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > TernaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprTextInternal(srcExpr, tokens, settings, TernaryOperatorPrecedence); tokens.Append(OperationToken, " ? "); tokens.AppendIntegerTextToken(instr, 1, 1); tokens.Append(OperationToken, " : "); tokens.AppendIntegerTextToken(instr, 0, 1); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_INT_TO_FLOAT: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } const auto floatType = instr.size == 2 ? "float16_t" : instr.size == 4 ? "float" : instr.size == 8 ? "double" : instr.size == 10 ? "long double" : "float" + std::to_string(instr.size) + "_t"; bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); tokens.Append(TypeNameToken, floatType.c_str()); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_INTRINSIC: [&]() { const auto intrinsic = instr.GetIntrinsic(); const auto intrinsicName = GetHighLevelILFunction()->GetArchitecture()->GetIntrinsicName(intrinsic); const auto parameterExprs = instr.GetParameterExprs(); tokens.Append(KeywordToken, intrinsicName, intrinsic); tokens.AppendOpenParen(); for (size_t index{}; index < parameterExprs.size(); index++) { const auto& parameterExpr = parameterExprs[index]; if (index != 0) tokens.Append(TextToken, ", "); GetExprTextInternal(parameterExpr, tokens, settings); } tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_RET: [&]() { const auto srcExprs = instr.GetSourceExprs(); tokens.Append(KeywordToken, "return"); for (size_t index{}; index < srcExprs.size(); index++) { const auto& srcExpr = srcExprs[index]; if (index == 0) tokens.Append(TextToken, " "); if (index != 0) tokens.Append(TextToken, ", "); GetExprTextInternal(srcExpr, tokens, settings); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_NORET: [&]() { tokens.Append(AnnotationToken, "/* no return */"); }(); break; case HLIL_UNREACHABLE: [&]() { tokens.Append(AnnotationToken, "/* unreachable */"); }(); break; case HLIL_JUMP: [&]() { const auto destExpr = instr.GetDestExpr(); tokens.Append(AnnotationToken, "/* jump -> "); GetExprTextInternal(destExpr, tokens, settings); tokens.Append(AnnotationToken, " */"); }(); break; case HLIL_UNDEF: [&]() { tokens.Append(AnnotationToken, "/* undefined */"); }(); break; case HLIL_TRAP: [&]() { const auto vector = instr.GetVector(); tokens.Append(KeywordToken, "trap"); tokens.AppendOpenParen(); tokens.AppendIntegerTextToken(instr, vector, 8); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_DEREF_FIELD: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto offset = instr.GetOffset(); const auto memberIndex = instr.GetMemberIndex(); auto type = srcExpr.GetType().GetValue(); if (type && (type->GetClass() == PointerTypeClass)) type = type->GetChildType().GetValue(); if (type && (type->GetClass() == NamedTypeReferenceClass)) type = GetFunction()->GetView()->GetTypeByRef(type->GetNamedTypeReference()); // Handle structure member access if (type && (type->GetClass() == StructureTypeClass)) { std::optional memberIndexHint; if (memberIndex != BN_INVALID_EXPR) memberIndexHint = memberIndex; bool outer = true; if (type->GetStructure()->ResolveMemberOrBaseMember(GetFunction()->GetView(), offset, 0, [&](NamedTypeReference*, Structure* s, size_t memberIndex, uint64_t structOffset, uint64_t adjustedOffset, const StructureMember& member) { BNSymbolDisplayResult symbolType; if (srcExpr.operation == HLIL_CONST_PTR) { const auto constant = srcExpr.GetConstant(); symbolType = tokens.AppendPointerTextToken( srcExpr, constant, settings, DisplaySymbolOnly, precedence); } else { GetExprTextInternal(srcExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); symbolType = OtherSymbolResult; } const auto displayDeref = symbolType != DataSymbolResult; if (displayDeref && outer) tokens.Append(OperationToken, "->"); else tokens.Append(OperationToken, "."); outer = false; vector nameList {member.name}; HighLevelILTokenEmitter::AddNamesForOuterStructureMembers( GetFunction()->GetView(), type, srcExpr, nameList); tokens.Append(FieldNameToken, member.name, structOffset + member.offset, 0, 0, BN_FULL_CONFIDENCE, nameList); }), memberIndexHint) return; } // For non-struct types or when struct member resolution fails, // render as pointer arithmetic: *[(type*)]([(char*)]expr[ + offset]) bool hasOffset = offset != 0; bool needsOuterParens = precedence > UnaryOperatorPrecedence; bool showTypeCasts = !settings || settings->IsOptionSet(ShowTypeCasts); if (needsOuterParens) tokens.AppendOpenParen(); tokens.Append(OperationToken, "*"); // Skip the outer cast if we're dereferencing a single byte and are // already casting to char* for the pointer arithmetic bool skipOuterCast = hasOffset && instr.size == 1; if (showTypeCasts && !skipOuterCast) { tokens.AppendOpenParen(); AppendSizeToken(hasOffset && type ? srcExpr.size : instr.size, true, tokens); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } if (hasOffset) { tokens.AppendOpenParen(); if (showTypeCasts) { tokens.AppendOpenParen(); tokens.Append(TypeNameToken, "char"); tokens.Append(TextToken, "*"); tokens.AppendCloseParen(); } } if (srcExpr.operation == HLIL_CONST_PTR) { const auto constant = srcExpr.GetConstant(); tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence); } else { GetExprTextInternal(srcExpr, tokens, settings, hasOffset ? AddOperatorPrecedence : UnaryOperatorPrecedence); } if (hasOffset) { tokens.Append(OperationToken, " + "); tokens.AppendIntegerTextToken(instr, offset, instr.size); tokens.AppendCloseParen(); } if (needsOuterParens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_EXTERN_PTR: [&]() { const int64_t val = instr.GetOffset(); if (val != 0) tokens.AppendOpenParen(); tokens.AppendPointerTextToken( instr, instr.GetConstant(), settings, AddressOfDataSymbols, precedence); if (val != 0) { char valStr[32]; if (val >= 0) { tokens.Append(OperationToken, " + "); if (val <= 9) snprintf(valStr, sizeof(valStr), "%" PRIx64, val); else snprintf(valStr, sizeof(valStr), "0x%" PRIx64, val); } else { tokens.Append(OperationToken, " - "); if (val >= -9) snprintf(valStr, sizeof(valStr), "%" PRIx64, -val); else snprintf(valStr, sizeof(valStr), "0x%" PRIx64, -val); } tokens.Append(IntegerToken, valStr, val); tokens.AppendCloseParen(); } if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_SYSCALL: [&]() { tokens.Append(KeywordToken, "syscall"); tokens.AppendOpenParen(); const auto operandList = instr.GetParameterExprs(); vector namedParams; bool skipSyscallNumber = false; if (GetFunction() && (operandList.size() > 0) && (operandList[0].operation == HLIL_CONST)) { const auto platform = GetFunction()->GetPlatform(); const auto view = GetFunction()->GetView(); if (platform) { const auto syscall = (uint32_t)operandList[0].GetConstant(); const auto syscallName = view->GetSystemCallName(platform, syscall); if (settings && settings->GetCallParameterHints() != NeverShowParameterHints) { const auto functionType = view->GetSystemCallType(platform, syscall); if (functionType && (functionType->GetClass() == FunctionTypeClass)) namedParams = functionType->GetParameters(); } if (syscallName.length()) { tokens.Append(TextToken, syscallName); tokens.Append(TextToken, " "); tokens.AppendOpenBrace(); GetExprTextInternal(operandList[0], tokens, settings); tokens.AppendCloseBrace(); skipSyscallNumber = true; } } } for (size_t i = (skipSyscallNumber ? 1 : 0); i < operandList.size(); i++) { if (i != 0) tokens.Append(TextToken, ", "); GetExprTextInternal(operandList[i], tokens, settings); } tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_BP: [&]() { tokens.Append(KeywordToken, "breakpoint"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_UNIMPL_MEM: case HLIL_UNIMPL: [&]() { const auto hlilFunc = GetHighLevelILFunction(); const auto instructionText = hlilFunc->GetExprText(hlilFunc->GetInstruction( hlilFunc->GetInstructionForExpr(instr.exprIndex)).exprIndex, true, settings); tokens.Append(AnnotationToken, "/* "); for (const auto& token : instructionText[0].tokens) tokens.Append(token.type, token.text, token.value); if (instructionText.size() > 1) tokens.Append(AnnotationToken, "..."); tokens.Append(AnnotationToken, " */"); }(); break; case HLIL_NOP: [&]() { tokens.Append(AnnotationToken, "/* nop */"); }(); break; case HLIL_GOTO: [&]() { const auto target = instr.GetTarget(); tokens.Append(KeywordToken, "goto "); tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_LABEL: [&]() { const auto target = instr.GetTarget(); // NB: Not using DecreaseIndent() here because it will mess up the indentation guides // that rely on matched calls to IncreaseIndent()/DecreaseIndent() to properly set // indentation groupings. Instead, we manually remove the last indent in the tokens tokens.InitLine(); auto newTokens = tokens.GetCurrentTokens(); bool foundIndent = false; bool erasedIndent = false; for (size_t i = 0; i < newTokens.size(); i++) { auto& token = newTokens[i]; if (token.type == IndentationToken) { foundIndent = true; } else if (foundIndent) { // We have indents and this is the first non-indent token after all the indents, // so remove the previous token. It must exist because we will have to have gone through // the condition above and looped at least once. newTokens.erase(newTokens.begin() + (i - 1)); erasedIndent = true; break; } } if (foundIndent && !erasedIndent) { // Found an indent, but it was the last token, so erase the last token newTokens.erase(newTokens.begin() + (newTokens.size() - 1)); } tokens.SetCurrentTokens(newTokens); tokens.Append(GotoLabelToken, GetFunction()->GetGotoLabelName(target), target); tokens.Append(TextToken, ":"); }(); break; case HLIL_LOW_PART: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprTextInternal(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.AppendOpenParen(); AppendSizeToken(instr.size, signedHint.value_or(true), tokens); tokens.AppendCloseParen(); GetExprTextInternal(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; case HLIL_SPLIT: [&]() { const auto low = instr.GetLowExpr(); const auto high = instr.GetHighExpr(); tokens.Append(OperationToken, "COMBINE"); tokens.AppendOpenParen(); GetExprTextInternal(high, tokens, settings); tokens.Append(TextToken, ", "); GetExprTextInternal(low, tokens, settings); tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); }(); break; default: [&]() { char buf[64]{}; snprintf(buf, sizeof(buf), "/* */", instr.operation); tokens.Append(AnnotationToken, buf); }(); break; } if (settings && settings->IsOptionSet(ShowILTypes) && instr.GetType().GetValue()) { tokens.AppendCloseParen(); } } void PseudoCFunction::GetExpr_CALL_OR_TAILCALL(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement) { const auto destExpr = instr.GetDestExpr(); const auto parameterExprs = instr.GetParameterExprs(); vector namedParams; Ref functionType = destExpr.GetType().GetValue(); if (functionType && (functionType->GetClass() == PointerTypeClass) && (functionType->GetChildType()->GetClass() == FunctionTypeClass)) namedParams = functionType->GetChildType()->GetParameters(); GetExprTextInternal(destExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendOpenParen(); for (size_t index {}; index < parameterExprs.size(); index++) { const auto& parameterExpr = parameterExprs[index]; if (index != 0) tokens.Append(TextToken, ", "); // If the type of the parameter is known to be a pointer to a string, then we directly render it as a // string, regardless of its length bool renderedAsString = false; if (index < namedParams.size() && parameterExprs[index].operation == HLIL_CONST_PTR) { auto exprType = namedParams[index].type; if (exprType.GetValue() && (exprType->GetClass() == PointerTypeClass)) { if (auto child = exprType->GetChildType(); child.GetValue()) { if ((child->IsInteger() && child->IsSigned() && child->GetWidth() == 1) || child->IsWideChar()) { tokens.AppendPointerTextToken(parameterExprs[index], parameterExprs[index].GetConstant(), settings, AddressOfDataSymbols, precedence, true); renderedAsString = true; } } } } if (!renderedAsString) GetExprText(parameterExpr, tokens, settings); } tokens.AppendCloseParen(); if (statement) tokens.AppendSemicolon(); } void PseudoCFunction::GetExpr_CONST_PTR(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement) { tokens.AppendPointerTextToken( instr, instr.GetConstant(), settings, AddressOfDataSymbols, precedence); if (statement) tokens.AppendSemicolon(); } void PseudoCFunction::GetExpr_IMPORT(const BinaryNinja::HighLevelILInstruction& instr, BinaryNinja::HighLevelILTokenEmitter& tokens, BinaryNinja::DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement) { const auto constant = instr.GetConstant(); auto symbol = GetHighLevelILFunction()->GetFunction()->GetView()->GetSymbolByAddress(constant); const auto symbolType = symbol->GetType(); if (symbol && (symbolType == ImportedDataSymbol || symbolType == ImportAddressSymbol)) { symbol = Symbol::ImportedFunctionFromImportAddressSymbol(symbol, constant); const auto symbolShortName = symbol->GetShortName(); tokens.Append(IndirectImportToken, NoTokenContext, symbolShortName, instr.address, constant, instr.size, instr.sourceOperand); return; } tokens.AppendPointerTextToken(instr, constant, settings, DereferenceNonDataSymbols, precedence); if (statement) tokens.AppendSemicolon(); } string PseudoCFunction::GetAnnotationStartString() const { // Show annotations as C-style inline comments return "/* "; } string PseudoCFunction::GetAnnotationEndString() const { // Show annotations as C-style inline comments return " */"; } TypePrinter* PseudoCFunction::GetTypePrinter() const { return m_typePrinter; } bool PseudoCFunction::ShouldSkipStatement(const BinaryNinja::HighLevelILInstruction& instr) { return false; } PseudoCFunctionType::PseudoCFunctionType(): LanguageRepresentationFunctionType("Pseudo C") { } PseudoCFunctionType::PseudoCFunctionType(const string& name) : LanguageRepresentationFunctionType(name) {} Ref PseudoCFunctionType::Create(Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) { return new PseudoCFunction(this, arch, owner, highLevelILFunction); }