#include #include "pseudorust.h" #include "rusttypes.h" #include "highlevelilinstruction.h" using namespace std; using namespace BinaryNinja; PseudoRustFunction::PseudoRustFunction(LanguageRepresentationFunctionType* type, Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) : LanguageRepresentationFunction(type, arch, owner, highLevelILFunction), m_highLevelIL(highLevelILFunction) { } void PseudoRustFunction::InitTokenEmitter(HighLevelILTokenEmitter& tokens) { // Braces must always be turned on for Rust tokens.SetBraceRequirement(BracesAlwaysRequired); // Multiple statements in a `match` require braces around them tokens.SetBracesAroundSwitchCases(true); // If the user hasn't specified a preference on brace placement, use the Rust standard style tokens.SetDefaultBracesOnSameLine(true); // Rust doesn't allow omitting the braces around conditional bodies tokens.SetSimpleScopeAllowed(false); tokens.SetHasCollapsableRegions(true); } void PseudoRustFunction::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.NewLine(); tokens.IncreaseIndent(); } } void PseudoRustFunction::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 PseudoRustFunction::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 PseudoRustFunction::GetSizeToken(size_t size, bool isSigned) { char sizeStr[32]; switch (size) { case 0: return {}; case 1: return (isSigned ? "i8" : "u8"); case 2: return (isSigned ? "i16" : "u16"); case 4: return (isSigned ? "i32" : "u32"); case 8: return (isSigned ? "i64" : "u64"); case 10: return (isSigned ? "i80" : "u80"); case 16: return (isSigned ? "i128" : "u128"); } snprintf(sizeStr, sizeof(sizeStr), "%s%" PRIuPTR, isSigned ? "i" : "u", size); return {sizeStr}; } void PseudoRustFunction::AppendSizeToken(size_t size, bool isSigned, HighLevelILTokenEmitter& emitter) { const auto token = GetSizeToken(size, isSigned); if (!token.empty()) emitter.Append(TypeNameToken, token); } void PseudoRustFunction::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 PseudoRustFunction::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 GetExprText(leftExpr, emitter, settings, precedence, InnerExpression, signedHint); emitter.Append(OperationToken, comparison); if (rightExpr.operation == HLIL_SPLIT) AppendDefaultSplitExpr(rightExpr, emitter, settings, precedence); else GetExprText(rightExpr, emitter, settings, precedence, InnerExpression, signedHint); } void PseudoRustFunction::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 (leftExpr.operation == HLIL_SPLIT) { const auto low = leftExpr.GetLowExpr(); const auto high = leftExpr.GetHighExpr(); emitter.Append(OperationToken, "COMBINE"); emitter.AppendOpenParen(); GetExprText(high, emitter, settings); emitter.Append(TextToken, ", "); GetExprText(low, emitter, settings); emitter.AppendCloseParen(); } if (operand == " + " || operand == " - ") { const auto exprType = leftExpr.GetType(); if (exprType.GetValue() && exprType->IsPointer()) { GetExprText(leftExpr, emitter, settings, MemberAndFunctionOperatorPrecedence); emitter.Append(TextToken, "."); emitter.Append(OperationToken, "byte_offset"); emitter.AppendOpenParen(); if (operand == " - ") { emitter.Append(OperationToken, "-"); GetExprText(rightExpr, emitter, settings, UnaryOperatorPrecedence); } else { GetExprText(rightExpr, emitter, settings); } emitter.AppendCloseParen(); return; } } GetExprText(leftExpr, emitter, settings, leftPrecedence, InnerExpression, 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); GetExprText(rightExpr, emitter, settings, precedence, InnerExpression, signedHint); } void PseudoRustFunction::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(); GetExprText(high, emitter, settings); emitter.Append(TextToken, ", "); GetExprText(low, emitter, settings); emitter.AppendCloseParen(); } GetExprText(leftExpr, emitter, settings); emitter.Append(TextToken, ", "); GetExprText(rightExpr, emitter, settings); emitter.AppendCloseParen(); } void PseudoRustFunction::AppendTwoOperandMethodCall(const string& function, const HighLevelILInstruction& instr, HighLevelILTokenEmitter& emitter, DisassemblySettings* settings) { const auto& twoOperand = instr.AsTwoOperand(); const auto leftExpr = twoOperand.GetLeftExpr(); const auto rightExpr = twoOperand.GetRightExpr(); if (leftExpr.operation == HLIL_SPLIT) { const auto low = leftExpr.GetLowExpr(); const auto high = leftExpr.GetHighExpr(); emitter.Append(OperationToken, "COMBINE"); emitter.AppendOpenParen(); GetExprText(high, emitter, settings); emitter.Append(TextToken, ", "); GetExprText(low, emitter, settings); emitter.AppendCloseParen(); } else { GetExprText(leftExpr, emitter, settings, MemberAndFunctionOperatorPrecedence); } emitter.Append(TextToken, "."); emitter.Append(OperationToken, function); emitter.AppendOpenParen(); GetExprText(rightExpr, emitter, settings); emitter.AppendCloseParen(); } void PseudoRustFunction::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(); GetExprText(high, tokens, settings); tokens.Append(TextToken, ", "); GetExprText(low, tokens, settings); tokens.AppendCloseParen(); } GetExprText(leftExpr, tokens, settings); tokens.Append(TextToken, ", "); GetExprText(rightExpr, tokens, settings); tokens.Append(TextToken, ", "); GetExprText(carryExpr, tokens, settings); tokens.AppendCloseParen(); } Ref PseudoRustFunction::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; } PseudoRustFunction::FieldDisplayType PseudoRustFunction::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; } void PseudoRustFunction::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(); tokens.AppendOpenParen(); tokens.Append(OperationToken, "&"); GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence); tokens.AppendCloseParen(); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "byte_offset"); tokens.AppendOpenParen(); tokens.AppendIntegerTextToken(instr, fieldOffset, instr.size); tokens.AppendCloseParen(); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.Append(KeywordToken, " as "); tokens.Append(TextToken, "*"); Ref srcType = srcExpr.GetType().GetValue(); if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) tokens.Append(KeywordToken, "const "); else tokens.Append(KeywordToken, "mut "); AppendSizeToken(!instr.size ? srcExpr.size : instr.size, signedHint.value_or(false), tokens); 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; bool castedSrcExpr = false; 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(); tokens.Append(OperationToken, "&"); GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); tokens.Append(TextToken, "*"); tokens.Append(KeywordToken, "mut "); AppendSizeToken(instr.size, signedHint.value_or(false), tokens); tokens.AppendCloseParen(); castedSrcExpr = true; precedence = MemberAndFunctionOperatorPrecedence; } 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, "&"); GetExprText(srcExpr, tokens, settings, addrOf ? UnaryOperatorPrecedence : LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, signedHint.value_or(false), tokens); castedSrcExpr = true; } else if (addrOf) { tokens.Append(OperationToken, "&"); } } else if (addrOf) { tokens.Append(OperationToken, "&"); } if (!castedSrcExpr) GetExprText(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 PseudoRustFunction::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(); GetExprText(high, tokens, settings, precedence); tokens.Append(OperationToken, " << "); tokens.Append(IntegerToken, std::to_string(low.size * 8)); tokens.AppendCloseParen(); tokens.Append(OperationToken, " | "); GetExprText(low, tokens, settings, precedence); if (precedence == EqualityOperatorPrecedence) tokens.AppendCloseParen(); } bool PseudoRustFunction::IsMutable(const Variable& var) const { for (auto i : GetHighLevelILFunction()->GetVariableDefinitions(var)) { auto expr = GetHighLevelILFunction()->GetExpr(i); if (expr.operation == HLIL_VAR_DECLARE || expr.operation == HLIL_VAR_INIT) continue; return true; } return GetHighLevelILFunction()->GetAliasedVariables().count(var) != 0; } void PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence, ExpressionType exprType, std::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(); RustTypePrinter printer; auto typeTokens = printer.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); 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 GetExprText(*i, tokens, settings, TopLevelOperatorPrecedence, exprType == TrailingStatementExpression && next == exprs.end() ? TrailingStatementExpression : StatementExpression); 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 "); // If the loop can be represented as a ranged for in idiomatic Rust, show it that way if (initExpr.operation == HLIL_VAR_INIT && (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_SLE || condExpr.operation == HLIL_CMP_ULT || condExpr.operation == HLIL_CMP_ULE) && condExpr.GetLeftExpr().operation == HLIL_VAR && condExpr.GetLeftExpr().GetVariable() == initExpr.GetDestVariable() && updateExpr.operation == HLIL_ASSIGN && updateExpr.GetDestExpr() == condExpr.GetLeftExpr() && updateExpr.GetSourceExpr().operation == HLIL_ADD && updateExpr.GetSourceExpr().GetLeftExpr() == condExpr.GetLeftExpr()) { bool stepBy = updateExpr.GetSourceExpr().GetRightExpr().operation != HLIL_CONST || updateExpr.GetSourceExpr().GetRightExpr().GetConstant() != 1; const auto variable = initExpr.GetDestVariable(); const auto variableName = GetHighLevelILFunction()->GetFunction()->GetVariableNameOrDefault(variable); tokens.Append(LocalVariableToken, LocalVariableTokenContext, variableName, instr.exprIndex, variable.ToIdentifier(), instr.size); tokens.Append(KeywordToken, " in "); if (stepBy) tokens.AppendOpenParen(); GetExprText( initExpr.GetSourceExpr(), tokens, settings, AssignmentOperatorPrecedence); if (condExpr.operation == HLIL_CMP_SLT || condExpr.operation == HLIL_CMP_ULT) tokens.Append(TextToken, ".."); else tokens.Append(TextToken, "..="); GetExprText(condExpr.GetRightExpr(), tokens, settings, AssignmentOperatorPrecedence); if (stepBy) { tokens.AppendCloseParen(); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "step_by"); tokens.AppendOpenParen(); GetExprText(updateExpr.GetSourceExpr().GetRightExpr(), tokens, settings); tokens.AppendCloseParen(); } } else { // For loop isn't directly representable in standard Rust if (initExpr.operation != HLIL_NOP) GetExprText(initExpr, tokens, settings); tokens.Append(TextToken, "; "); if (condExpr.operation != HLIL_NOP) GetExprText(condExpr, tokens, settings); tokens.Append(TextToken, "; "); if (updateExpr.operation != HLIL_NOP) GetExprText(updateExpr, tokens, settings); } if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprText(loopExpr, tokens, settings, TopLevelOperatorPrecedence, StatementExpression); tokens.EndScope(scopeType); tokens.FinalizeScope(); } } else { tokens.Append(KeywordToken, "while "); GetExprText(condExpr, tokens, settings); } }(); break; case HLIL_IF: [&]() { const auto condExpr = instr.GetConditionExpr(); const auto trueExpr = instr.GetTrueExpr(); const auto falseExpr = instr.GetFalseExpr(); tokens.Append(KeywordToken, "if "); GetExprText(condExpr, tokens, settings); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(trueExpr); tokens.BeginScope(scopeType); GetExprText(trueExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); tokens.EndScope(scopeType); } //tokens.SetCurrentExpr(falseExpr); if (falseExpr.operation == HLIL_IF) { tokens.ScopeContinuation(false); tokens.Append(KeywordToken, "else "); GetExprText(falseExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); } 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, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(falseExpr); tokens.BeginScope(scopeType); GetExprText(falseExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); tokens.EndScope(scopeType); tokens.FinalizeScope(); } } else { tokens.FinalizeScope(); } }(); break; case HLIL_WHILE: [&]() { const auto condExpr = instr.GetConditionExpr(); const auto loopExpr = instr.GetLoopExpr(); if (condExpr.operation == HLIL_CONST && condExpr.GetConstant() != 0) { tokens.Append(KeywordToken, "loop"); } else { tokens.Append(KeywordToken, "while "); GetExprText(condExpr, tokens, settings); } if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprText(loopExpr, tokens, settings, TopLevelOperatorPrecedence, StatementExpression); 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 "); GetExprText(condExpr, tokens, settings); tokens.Append(KeywordToken, ";"); } else { auto scopeType = HighLevelILFunction::GetExprScopeType(loopExpr); tokens.BeginScope(scopeType); GetExprText(loopExpr, tokens, settings, TopLevelOperatorPrecedence, StatementExpression); tokens.EndScope(scopeType); tokens.ScopeContinuation(true); tokens.Append(KeywordToken, "while "); GetExprText(condExpr, tokens, settings); tokens.Append(KeywordToken, ";"); tokens.FinalizeScope(); } } else { tokens.Append(TextToken, "/* do */ "); tokens.Append(KeywordToken, "while "); GetExprText(condExpr, tokens, settings); } }(); break; case HLIL_SWITCH: [&]() { const auto condExpr = instr.GetConditionExpr(); const auto caseExprs = instr.GetCases(); const auto defaultExpr = instr.GetDefaultExpr(); tokens.Append(KeywordToken, "match "); GetExprText(condExpr, tokens, settings); tokens.BeginScope(SwitchScopeType); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { for (const auto caseExpr: caseExprs) { GetExprText(caseExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); tokens.NewLine(); } // Check for default case if (defaultExpr.operation != HLIL_NOP && defaultExpr.operation != HLIL_UNREACHABLE) { tokens.PrependCollapseIndicator(function, instr, 1); tokens.Append(TextToken, "_ =>"); if (function->IsInstructionCollapsed(instr, 1)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { tokens.BeginScope(CaseScopeType); GetExprText(defaultExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); 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]; if (index != 0) tokens.Append(TextToken, " | "); GetExprText(valueExpr, tokens, settings); } tokens.Append(TextToken, " =>"); if (!instr.ast) return; if (function->IsInstructionCollapsed(instr)) { tokens.Append(CollapsedInformationToken, " {...}"); } else { tokens.BeginScope(CaseScopeType); GetExprText(trueExpr, tokens, settings, TopLevelOperatorPrecedence, exprType); tokens.EndScope(CaseScopeType); tokens.FinalizeScope(); } }(); break; case HLIL_BREAK: [&]() { tokens.Append(KeywordToken, "break"); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_CONTINUE: [&]() { tokens.Append(KeywordToken, "continue"); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ZX: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence, InnerExpression, false); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, false, tokens); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_SX: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence, InnerExpression, true); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, true, tokens); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_CALL: [&]() { const auto destExpr = instr.GetDestExpr(); const auto parameterExprs = instr.GetParameterExprs(); GetExprText(destExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendOpenParen(); vector namedParams; Ref functionType = instr.GetDestExpr().GetType().GetValue(); if (functionType && (functionType->GetClass() == PointerTypeClass) && (functionType->GetChildType()->GetClass() == FunctionTypeClass)) namedParams = functionType->GetChildType()->GetParameters(); 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_IMPORT: [&]() { 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ARRAY_INDEX: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto indexExpr = instr.GetIndexExpr(); GetExprText(srcExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendOpenBracket(); GetExprText(indexExpr, tokens, settings); tokens.AppendCloseBracket(); if (exprType != InnerExpression) 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(); RustTypePrinter printer; const auto prevTypeTokens = variableType.GetValue() ? printer.GetTypeTokensBeforeName(variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; const auto postTypeTokens = variableType.GetValue() ? printer.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(); tokens.Append(KeywordToken, "let "); // Only show `mut` keyword if the variable is actually changed if (IsMutable(destExpr)) tokens.Append(KeywordToken, "mut "); if (variableType.GetValue()) { for (auto typeToken : prevTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = destExpr.ToIdentifier(); tokens.Append(typeToken); } } 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; } GetExprText(srcExpr, tokens, settings, AssignmentOperatorPrecedence); if (appearsDead) tokens.EndForceZeroConfidence(); if (exprType != InnerExpression) 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(); RustTypePrinter printer; const auto prevTypeTokens = variableType.GetValue() ? printer.GetTypeTokensBeforeName(variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; const auto postTypeTokens = variableType.GetValue() ? printer.GetTypeTokensAfterName(variableType.GetValue(), platform, variableType.GetConfidence()) : vector {}; tokens.Append(KeywordToken, "let "); // Only show `mut` keyword if the variable is actually changed if (IsMutable(variable)) tokens.Append(KeywordToken, "mut "); if (variableType.GetValue()) { for (auto typeToken: prevTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = variable.ToIdentifier(); tokens.Append(typeToken); } } tokens.AppendVarTextToken(variable, instr, instr.size); if (variableType.GetValue()) { for (auto typeToken: postTypeTokens) { typeToken.context = LocalVariableTokenContext; typeToken.address = variable.ToIdentifier(); tokens.Append(typeToken); } } if (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_CONST: [&]() { tokens.AppendConstantTextToken(instr, instr.GetConstant(), instr.size, settings, precedence); if (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_CONST_PTR: [&]() { tokens.AppendPointerTextToken( instr, instr.GetConstant(), settings, AddressOfDataSymbols, precedence); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); 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 (exprType != InnerExpression) 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 assignSignHint; if (destIsSplit) { const auto high = destExpr.GetHighExpr(); const auto low = destExpr.GetLowExpr(); GetExprText(high, tokens, settings, precedence); tokens.Append(OperationToken, " = "); tokens.Append(OperationToken, "HIGH"); AppendSingleSizeToken(high.size, OperationToken, tokens); tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, precedence); tokens.AppendCloseParen(); tokens.AppendSemicolon(); tokens.NewLine(); GetExprText(low, tokens, settings, precedence); } else if (srcExpr.operation == HLIL_SPLIT) { GetExprText(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 = " /= "; assignUpdateSource = srcExpr.GetRightExpr(); assignSignHint = false; break; case HLIL_DIVS: assignUpdateOperator = " /= "; assignUpdateSource = srcExpr.GetRightExpr(); assignSignHint = false; break; case HLIL_LSL: assignUpdateOperator = " <<= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_LSR: assignUpdateOperator = " >>= "; assignUpdateSource = srcExpr.GetRightExpr(); break; case HLIL_ASR: assignUpdateOperator = " >>= "; 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; } } } GetExprText(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 { GetExprText(assignUpdateSource.value(), tokens, settings, AssignmentOperatorPrecedence, InnerExpression, assignSignHint); } } else { GetExprText(srcExpr, tokens, settings, AssignmentOperatorPrecedence, InnerExpression, assignSignHint); } if (destIsSplit) tokens.AppendCloseParen(); if (appearsDead) tokens.EndForceZeroConfidence(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ASSIGN_UNPACK: [&]() { const auto srcExpr = instr.GetSourceExpr(); const auto destExprs = instr.GetDestExprs(); const auto firstExpr = destExprs[0]; GetExprText(firstExpr, tokens, settings, AssignmentOperatorPrecedence); tokens.Append(OperationToken, " = "); GetExprText(srcExpr, tokens, settings, AssignmentOperatorPrecedence); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_STRUCT_FIELD: [&]() { AppendFieldTextTokens(instr, tokens, settings, signedHint, false); if (exprType != InnerExpression) 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(); const auto type = srcExpr.GetType(); BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence; if (type.GetValue() && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth() && (!settings || settings->IsOptionSet(ShowTypeCasts))) srcPrecedence = LowUnaryOperatorPrecedence; vector pointerTokens{}; if (AppendPointerTextToken(instr, constant, pointerTokens, settings, DereferenceNonDataSymbols, srcPrecedence) == DataSymbolResult) { if (type.GetValue() && type->GetClass() == PointerTypeClass && instr.size != type->GetChildType()->GetWidth()) { tokens.Append(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) tokens.AppendOpenParen(); for (const auto& token : pointerTokens) { appendMaybeBrace(token); } if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.Append(KeywordToken, " as "); tokens.Append(TextToken, "*"); Ref srcType = srcExpr.GetType().GetValue(); if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) tokens.Append(KeywordToken, "const "); else tokens.Append(KeywordToken, "mut "); AppendSizeToken(instr.size, false, tokens); tokens.AppendCloseParen(); } } else { for (const auto& token : pointerTokens) { appendMaybeBrace(token); } } } else { for (const auto& token : pointerTokens) { appendMaybeBrace(token); } } } else { vector derefConst; Ref srcType = srcExpr.GetType().GetValue(); derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()); while (srcExpr.operation == HLIL_DEREF) { auto next = srcExpr.GetSourceExpr(); if (next.size == srcExpr.size) { srcType = srcExpr.GetType().GetValue(); derefConst.push_back(srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()); srcExpr = srcExpr.GetSourceExpr(); } else { break; } } bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); for (size_t index = 0; index < derefConst.size(); index++) tokens.Append(OperationToken, "*"); BNOperatorPrecedence srcPrecedence = UnaryOperatorPrecedence; if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.AppendOpenParen(); srcPrecedence = LowUnaryOperatorPrecedence; } GetExprText(srcExpr, tokens, settings, srcPrecedence); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.Append(KeywordToken, " as "); for (auto isConst : derefConst) { tokens.Append(TextToken, "*"); tokens.Append(KeywordToken, isConst ? "const ": "mut "); } AppendSizeToken(instr.size, false, tokens); tokens.AppendCloseParen(); } if (parens) tokens.AppendCloseParen(); } if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_TAILCALL: [&]() { const auto destExpr = instr.GetDestExpr(); const auto parameterExprs = instr.GetParameterExprs(); tokens.Append(AnnotationToken, "/* tailcall */"); tokens.NewLine(); if (exprType != TrailingStatementExpression) tokens.Append(KeywordToken, "return "); GetExprText(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, ", "); GetExprText(parameterExpr, tokens, settings); } tokens.AppendCloseParen(); if (exprType == StatementExpression) tokens.AppendSemicolon(); }(); 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, "&"); GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence); } if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_E: case HLIL_CMP_E: [&]() { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" == ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_NE: case HLIL_CMP_NE: [&]() { bool parens = precedence > EqualityOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendComparison(" != ", instr, tokens, settings, EqualityOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ADC: case HLIL_ADD_OVERFLOW: case HLIL_FADD: case HLIL_ADD: [&]() { const auto leftType = instr.GetLeftExpr().GetType(); bool parens; BNOperatorPrecedence opPrecedence = AddOperatorPrecedence; if (leftType.GetValue() && leftType->IsPointer()) { parens = false; opPrecedence = MemberAndFunctionOperatorPrecedence; } else { parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; } if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" + ", instr, tokens, settings, opPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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(); GetExprText(left, tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.AppendCloseParen(); return; } } const auto leftType = instr.GetLeftExpr().GetType(); bool parens; BNOperatorPrecedence opPrecedence = SubOperatorPrecedence; if (leftType.GetValue() && leftType->IsPointer()) { parens = false; opPrecedence = MemberAndFunctionOperatorPrecedence; } else { parens = precedence > AddOperatorPrecedence || precedence == ShiftOperatorPrecedence || precedence == BitwiseAndOperatorPrecedence || precedence == BitwiseOrOperatorPrecedence || precedence == BitwiseXorOperatorPrecedence; } if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" - ", instr, tokens, settings, opPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_LSL: [&]() { bool parens = precedence > ShiftOperatorPrecedence; if (parens) tokens.AppendOpenParen(); AppendTwoOperand(" << ", instr, tokens, settings, ShiftOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ROR: [&]() { AppendTwoOperandMethodCall("rotate_right", instr, tokens, settings); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ROL: [&]() { AppendTwoOperandMethodCall("rotate_left", instr, tokens, settings); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_RLC: [&]() { AppendTwoOperandFunctionWithCarry("RLC", instr, tokens, settings); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_RRC: [&]() { AppendTwoOperandFunctionWithCarry("RRC", instr, tokens, settings); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_TEST_BIT: [&]() { AppendTwoOperandFunction("TEST_BIT", instr, tokens, settings); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FLOOR: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "floor"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_CEIL: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "ceil"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FTRUNC: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "trunc"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FABS: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "abs"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FSQRT: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "sqrt"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_ROUND_TO_INT: [&]() { GetExprText(instr.GetSourceExpr(), tokens, settings, MemberAndFunctionOperatorPrecedence); tokens.Append(TextToken, "."); tokens.Append(OperationToken, "round"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_O: [&]() { AppendTwoOperandFunction("FCMP_O", instr, tokens, settings, false); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FCMP_UO: [&]() { AppendTwoOperandFunction("FCMP_UO", instr, tokens, settings, false); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_NOT: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.Append(OperationToken, "!"); GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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(); GetExprText(srcExpr, tokens, settings, UnaryOperatorPrecedence, InnerExpression, true); tokens.AppendCloseParen(); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FLOAT_CONV: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } const auto floatType = "f" + std::to_string(instr.size * 8); bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); tokens.Append(TypeNameToken, floatType.c_str()); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_FLOAT_TO_INT: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, true, tokens); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_BOOL_TO_INT: [&]() { const auto srcExpr = instr.GetSourceExpr(); bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, true, tokens); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_INT_TO_FLOAT: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } const auto floatType = "f" + std::to_string(instr.size * 8); bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); tokens.Append(TypeNameToken, floatType.c_str()); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) 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, ", "); GetExprText(parameterExpr, tokens, settings); } tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_RET: [&]() { const auto srcExprs = instr.GetSourceExprs(); if (!instr.ast || exprType != TrailingStatementExpression) tokens.Append(KeywordToken, "return"); if (srcExprs.size() != 0) { if (!instr.ast || exprType != TrailingStatementExpression) tokens.Append(TextToken, " "); if (srcExprs.size() > 1) tokens.AppendOpenParen(); for (size_t index = 0; index < srcExprs.size(); index++) { const auto& srcExpr = srcExprs[index]; if (index != 0) tokens.Append(TextToken, ", "); GetExprText(srcExpr, tokens, settings); } if (srcExprs.size() > 1) tokens.AppendCloseParen(); } if (exprType == StatementExpression) 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 -> "); GetExprText(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 (exprType != InnerExpression) 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()); bool derefOffset = false; 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 { GetExprText(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; } else if (type && (type->GetClass() == StructureTypeClass)) { derefOffset = true; } if (derefOffset || offset != 0) { bool parens = precedence > UnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); tokens.Append(OperationToken, "*"); if (!settings || settings->IsOptionSet(ShowTypeCasts)) tokens.AppendOpenParen(); if (srcExpr.operation == HLIL_CONST_PTR) { const auto constant = srcExpr.GetConstant(); tokens.AppendPointerTextToken(srcExpr, constant, settings, DisplaySymbolOnly, precedence); } else { GetExprText(srcExpr, tokens, settings, MemberAndFunctionOperatorPrecedence); } tokens.Append(TextToken, "."); tokens.Append(OperationToken, "byte_offset"); tokens.AppendOpenParen(); tokens.AppendIntegerTextToken(instr, offset, instr.size); tokens.AppendCloseParen(); if (!settings || settings->IsOptionSet(ShowTypeCasts)) { tokens.Append(KeywordToken, " as "); tokens.Append(TextToken, "*"); Ref srcType = srcExpr.GetType().GetValue(); if (srcType && srcType->IsPointer() && srcType->GetChildType()->IsConst()) tokens.Append(KeywordToken, "const "); else tokens.Append(KeywordToken, "mut "); AppendSizeToken(!derefOffset ? srcExpr.size : instr.size, true, tokens); tokens.AppendCloseParen(); } if (parens) tokens.AppendCloseParen(); } if (exprType != InnerExpression) 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 (exprType != InnerExpression) 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(); GetExprText(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, ", "); GetExprText(operandList[i], tokens, settings); } tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_BP: [&]() { tokens.Append(KeywordToken, "breakpoint"); tokens.AppendOpenParen(); tokens.AppendCloseParen(); if (exprType != InnerExpression) 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 (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_LABEL: [&]() { const auto target = instr.GetTarget(); tokens.DecreaseIndent(); tokens.Append(GotoLabelToken, "'" + GetFunction()->GetGotoLabelName(target), target); tokens.Append(TextToken, ":"); tokens.IncreaseIndent(); }(); break; case HLIL_LOW_PART: [&]() { const auto srcExpr = instr.GetSourceExpr(); if (settings && !settings->IsOptionSet(ShowTypeCasts)) { GetExprText(srcExpr, tokens, settings, precedence); return; } bool parens = precedence > LowUnaryOperatorPrecedence; if (parens) tokens.AppendOpenParen(); GetExprText(srcExpr, tokens, settings, LowUnaryOperatorPrecedence); tokens.Append(KeywordToken, " as "); AppendSizeToken(instr.size, signedHint.value_or(true), tokens); if (parens) tokens.AppendCloseParen(); if (exprType != InnerExpression) tokens.AppendSemicolon(); }(); break; case HLIL_SPLIT: 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 PseudoRustFunction::GetExprText(const HighLevelILInstruction& instr, HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence, bool statement) { GetExprText(instr, tokens, settings, precedence, statement ? TrailingStatementExpression : InnerExpression); } string PseudoRustFunction::GetAnnotationStartString() const { // Show annotations as Rust-style inline comments return "/* "; } string PseudoRustFunction::GetAnnotationEndString() const { // Show annotations as Rust-style inline comments return " */"; } PseudoRustFunctionType::PseudoRustFunctionType(): LanguageRepresentationFunctionType("Pseudo Rust") { // Create a type printer for Rust-style types and register it m_typePrinter = new RustTypePrinter(); TypePrinter::Register(m_typePrinter); } Ref PseudoRustFunctionType::Create(Architecture* arch, Function* owner, HighLevelILFunction* highLevelILFunction) { return new PseudoRustFunction(this, arch, owner, highLevelILFunction); } Ref PseudoRustFunctionType::GetTypePrinter() { // Return the Rust type printer as the default type printer for this language return m_typePrinter; } vector PseudoRustFunctionType::GetFunctionTypeTokens(Function* func, DisassemblySettings* settings) { vector result; DisassemblyTextLine line; line.addr = func->GetStart(); RustTypePrinter printer; Ref funcType = func->GetType(); if (!funcType) return {}; // Use the Rust type printer to generate a Rust formatted function declaration vector before = printer.GetTypeTokensBeforeName(funcType, func->GetPlatform()); vector after = printer.GetTypeTokensAfterNameInternal(funcType, func->GetPlatform(), BN_FULL_CONFIDENCE, nullptr, NoTokenEscapingType, true); line.tokens = before; if (!before.empty()) line.tokens.emplace_back(TextToken, " "); Ref sym = func->GetSymbol(); line.tokens.emplace_back(CodeSymbolToken, sym->GetShortName(), func->GetStart()); line.tokens.insert(line.tokens.end(), after.begin(), after.end()); return {line}; } extern "C" { BN_DECLARE_CORE_ABI_VERSION BINARYNINJAPLUGIN void CorePluginDependencies() { } BINARYNINJAPLUGIN bool CorePluginInit() { LanguageRepresentationFunctionType* type = new PseudoRustFunctionType(); LanguageRepresentationFunctionType::Register(type); return true; } }